feishu-mcp 0.0.7 → 0.0.9-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/config.js CHANGED
@@ -1,26 +1,104 @@
1
- import { Config, ConfigSource } from './utils/config.js';
2
- /**
3
- * 为了向后兼容,保留getServerConfig函数
4
- * 但内部使用Config类
5
- * @param isStdioMode 是否在stdio模式下
6
- * @returns 服务器配置
7
- */
1
+ import { config } from "dotenv";
2
+ import yargs from "yargs";
3
+ import { hideBin } from "yargs/helpers";
4
+ // 确保在任何配置读取前加载.env文件
5
+ config();
6
+ function maskApiKey(key) {
7
+ if (key.length <= 4)
8
+ return "****";
9
+ return `****${key.slice(-4)}`;
10
+ }
8
11
  export function getServerConfig(isStdioMode) {
9
- const config = Config.getInstance();
10
- if (!isStdioMode) {
11
- config.printConfig(isStdioMode);
12
- }
13
- // 为了向后兼容,返回旧格式的配置对象
14
- return {
15
- port: config.server.port,
16
- feishuAppId: config.feishu.appId,
17
- feishuAppSecret: config.feishu.appSecret,
12
+ // Parse command line arguments
13
+ const argv = yargs(hideBin(process.argv))
14
+ .options({
15
+ port: {
16
+ type: "number",
17
+ description: "Port to run the server on",
18
+ },
19
+ "feishu-app-id": {
20
+ type: "string",
21
+ description: "Feishu App ID",
22
+ },
23
+ "feishu-app-secret": {
24
+ type: "string",
25
+ description: "Feishu App Secret",
26
+ },
27
+ })
28
+ .help()
29
+ .parseSync();
30
+ const config = {
31
+ port: 3333,
18
32
  configSources: {
19
- port: config.configSources['server.port'].toLowerCase(),
20
- feishuAppId: config.configSources['feishu.appId']?.toLowerCase(),
21
- feishuAppSecret: config.configSources['feishu.appSecret']?.toLowerCase()
22
- }
33
+ port: "default",
34
+ },
23
35
  };
36
+ // Handle PORT
37
+ if (argv.port) {
38
+ config.port = argv.port;
39
+ config.configSources.port = "cli";
40
+ }
41
+ else if (process.env.PORT) {
42
+ config.port = parseInt(process.env.PORT, 10);
43
+ config.configSources.port = "env";
44
+ }
45
+ // 在加载环境变量之前添加日志
46
+ console.log('开始加载环境变量配置...');
47
+ console.log('当前环境变量 FEISHU_APP_ID:', process.env.FEISHU_APP_ID);
48
+ console.log('当前环境变量 FEISHU_APP_SECRET:', process.env.FEISHU_APP_SECRET);
49
+ // Handle Feishu configuration
50
+ if (argv["feishu-app-id"]) {
51
+ config.feishuAppId = argv["feishu-app-id"];
52
+ config.configSources.feishuAppId = "cli";
53
+ console.log(`飞书应用 ID 来自命令行参数: ${maskApiKey(config.feishuAppId)}`);
54
+ }
55
+ else if (process.env.FEISHU_APP_ID) {
56
+ config.feishuAppId = process.env.FEISHU_APP_ID;
57
+ config.configSources.feishuAppId = "env";
58
+ console.log(`飞书应用 ID 来自环境变量: ${maskApiKey(config.feishuAppId)}`);
59
+ }
60
+ else {
61
+ console.log('未提供飞书应用 ID');
62
+ }
63
+ if (argv["feishu-app-secret"]) {
64
+ config.feishuAppSecret = argv["feishu-app-secret"];
65
+ config.configSources.feishuAppSecret = "cli";
66
+ console.log(`飞书应用密钥来自命令行参数: ${maskApiKey(config.feishuAppSecret)}`);
67
+ }
68
+ else if (process.env.FEISHU_APP_SECRET) {
69
+ config.feishuAppSecret = process.env.FEISHU_APP_SECRET;
70
+ config.configSources.feishuAppSecret = "env";
71
+ console.log(`飞书应用密钥来自环境变量: ${maskApiKey(config.feishuAppSecret)}`);
72
+ }
73
+ else {
74
+ console.log('未提供飞书应用密钥');
75
+ }
76
+ // 输出飞书配置状态总结
77
+ if (config.feishuAppId && config.feishuAppSecret) {
78
+ console.log('飞书配置已完整提供,服务将被初始化');
79
+ }
80
+ else if (config.feishuAppId || config.feishuAppSecret) {
81
+ console.log('飞书配置不完整,服务将不会初始化');
82
+ }
83
+ else {
84
+ console.log('未提供飞书配置,服务将不会初始化');
85
+ }
86
+ // 验证配置
87
+ if (!config.feishuAppId || !config.feishuAppSecret) {
88
+ console.error("FEISHU_APP_ID 和 FEISHU_APP_SECRET 是必需的(通过命令行参数 --feishu-app-id 和 --feishu-app-secret 或 .env 文件)");
89
+ process.exit(1);
90
+ }
91
+ // Log configuration sources
92
+ if (!isStdioMode) {
93
+ console.log("\n配置信息:");
94
+ console.log(`- PORT: ${config.port} (来源: ${config.configSources.port})`);
95
+ if (config.feishuAppId) {
96
+ console.log(`- FEISHU_APP_ID: ${maskApiKey(config.feishuAppId)} (来源: ${config.configSources.feishuAppId})`);
97
+ }
98
+ if (config.feishuAppSecret) {
99
+ console.log(`- FEISHU_APP_SECRET: ${maskApiKey(config.feishuAppSecret)} (来源: ${config.configSources.feishuAppSecret})`);
100
+ }
101
+ console.log(); // 空行,提高可读性
102
+ }
103
+ return config;
24
104
  }
25
- // 导出Config类
26
- export { Config, ConfigSource };
@@ -0,0 +1,104 @@
1
+ import { Logger } from '../utils/logger';
2
+ /**
3
+ * SSE连接管理器 - 负责管理所有的SSE长连接和心跳机制
4
+ */
5
+ export class SSEConnectionManager {
6
+ constructor() {
7
+ Object.defineProperty(this, "transports", {
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true,
11
+ value: {}
12
+ });
13
+ Object.defineProperty(this, "connections", {
14
+ enumerable: true,
15
+ configurable: true,
16
+ writable: true,
17
+ value: new Map()
18
+ });
19
+ Object.defineProperty(this, "keepAliveIntervalId", {
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true,
23
+ value: null
24
+ });
25
+ Object.defineProperty(this, "KEEP_ALIVE_INTERVAL_MS", {
26
+ enumerable: true,
27
+ configurable: true,
28
+ writable: true,
29
+ value: 1000 * 25
30
+ }); // 25秒心跳间隔
31
+ this.startGlobalKeepAlive();
32
+ }
33
+ /**
34
+ * 启动全局心跳管理
35
+ */
36
+ startGlobalKeepAlive() {
37
+ if (this.keepAliveIntervalId) {
38
+ clearInterval(this.keepAliveIntervalId);
39
+ }
40
+ this.keepAliveIntervalId = setInterval(() => {
41
+ Logger.info(`[KeepAlive] Sending keepalive to ${this.connections.size} connections`);
42
+ for (const [sessionId, connection] of this.connections.entries()) {
43
+ if (!connection.res.writableEnded) {
44
+ connection.res.write(': keepalive\n\n');
45
+ }
46
+ else {
47
+ // 移除已关闭的连接
48
+ this.removeConnection(sessionId);
49
+ }
50
+ }
51
+ }, this.KEEP_ALIVE_INTERVAL_MS);
52
+ }
53
+ /**
54
+ * 添加新的SSE连接
55
+ */
56
+ addConnection(sessionId, transport, req, res) {
57
+ this.transports[sessionId] = transport;
58
+ this.connections.set(sessionId, { res });
59
+ console.info(`[SSE Connection] Client connected: ${sessionId}`);
60
+ req.on('close', () => {
61
+ this.removeConnection(sessionId);
62
+ });
63
+ }
64
+ /**
65
+ * 移除SSE连接
66
+ */
67
+ removeConnection(sessionId) {
68
+ const transport = this.transports[sessionId];
69
+ if (transport) {
70
+ try {
71
+ transport.close();
72
+ Logger.info(`[SSE Connection] Transport closed for: ${sessionId}`);
73
+ }
74
+ catch (error) {
75
+ Logger.error(`[SSE Connection] Error closing transport for: ${sessionId}`, error);
76
+ }
77
+ }
78
+ delete this.transports[sessionId];
79
+ this.connections.delete(sessionId);
80
+ console.info(`[SSE Connection] Client disconnected: ${sessionId}`);
81
+ }
82
+ /**
83
+ * 获取指定sessionId的传输实例
84
+ */
85
+ getTransport(sessionId) {
86
+ console.info(`[SSE Connection] Getting transport for sessionId: ${sessionId}`);
87
+ return this.transports[sessionId];
88
+ }
89
+ /**
90
+ * 关闭连接管理器
91
+ */
92
+ shutdown() {
93
+ if (this.keepAliveIntervalId) {
94
+ clearInterval(this.keepAliveIntervalId);
95
+ this.keepAliveIntervalId = null;
96
+ }
97
+ // 关闭所有连接
98
+ Logger.info(`[SSE Connection] Shutting down all connections (${this.connections.size} active)`);
99
+ for (const sessionId of this.connections.keys()) {
100
+ this.removeConnection(sessionId);
101
+ }
102
+ Logger.info(`[SSE Connection] All connections closed`);
103
+ }
104
+ }
@@ -0,0 +1,67 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { FeishuApiService } from '../services/feishuApiService.js';
3
+ import { Logger } from '../utils/logger.js';
4
+ import { registerFeishuTools } from './tools/feishuTools.js';
5
+ import { registerFeishuBlockTools } from './tools/feishuBlockTools.js';
6
+ import { registerFeishuFolderTools } from './tools/feishuFolderTools.js';
7
+ const serverInfo = {
8
+ name: "Feishu MCP Server",
9
+ version: "0.0.9-alpha",
10
+ };
11
+ const serverOptions = {
12
+ capabilities: { logging: {}, tools: {} },
13
+ };
14
+ /**
15
+ * 飞书MCP服务类
16
+ * 继承自McpServer,提供飞书工具注册和初始化功能
17
+ */
18
+ export class FeishuMcp extends McpServer {
19
+ /**
20
+ * 构造函数
21
+ */
22
+ constructor() {
23
+ super(serverInfo, serverOptions);
24
+ Object.defineProperty(this, "feishuService", {
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true,
28
+ value: null
29
+ });
30
+ // 初始化飞书服务
31
+ this.initFeishuService();
32
+ // 注册所有工具
33
+ if (this.feishuService) {
34
+ this.registerAllTools();
35
+ }
36
+ else {
37
+ Logger.error('无法注册飞书工具: 飞书服务初始化失败');
38
+ throw new Error('飞书服务初始化失败');
39
+ }
40
+ }
41
+ /**
42
+ * 初始化飞书API服务
43
+ */
44
+ initFeishuService() {
45
+ try {
46
+ // 使用单例模式获取飞书服务实例
47
+ this.feishuService = FeishuApiService.getInstance();
48
+ Logger.info('飞书服务初始化成功');
49
+ }
50
+ catch (error) {
51
+ Logger.error('飞书服务初始化失败:', error);
52
+ this.feishuService = null;
53
+ }
54
+ }
55
+ /**
56
+ * 注册所有飞书MCP工具
57
+ */
58
+ registerAllTools() {
59
+ if (!this.feishuService) {
60
+ return;
61
+ }
62
+ // 注册所有工具
63
+ registerFeishuTools(this, this.feishuService);
64
+ registerFeishuBlockTools(this, this.feishuService);
65
+ registerFeishuFolderTools(this, this.feishuService);
66
+ }
67
+ }