Skip to main content
Glama

Feishu MCP Server

config.ts13.1 kB
import { config as loadDotEnv } from 'dotenv'; import { hideBin } from 'yargs/helpers'; import yargs from 'yargs'; import { Logger, LogLevel } from './logger.js'; /** * 配置来源枚举 */ export enum ConfigSource { DEFAULT = 'default', ENV = 'env', CLI = 'cli', FILE = 'file' } /** * 服务器配置接口 */ export interface ServerConfig { port: number; } /** * 飞书配置接口 */ export interface FeishuConfig { appId: string; appSecret: string; baseUrl: string; authType: 'tenant' | 'user'; tokenEndpoint: string; } /** * 日志配置接口 */ export interface LogConfig { level: LogLevel; showTimestamp: boolean; showLevel: boolean; timestampFormat: string; } /** * 缓存配置接口 */ export interface CacheConfig { enabled: boolean; ttl: number; // 单位:秒 maxSize: number; // 最大缓存条目数 } /** * 应用配置管理类 * 统一管理所有配置,支持环境变量、命令行参数和默认值 */ export class Config { private static instance: Config; public readonly server: ServerConfig; public readonly feishu: FeishuConfig; public readonly log: LogConfig; public readonly cache: CacheConfig; public readonly configSources: { [key: string]: ConfigSource; }; /** * 私有构造函数,用于单例模式 */ private constructor() { // 确保在任何配置读取前加载.env文件 loadDotEnv(); // 解析命令行参数 const argv = this.parseCommandLineArgs(); // 初始化配置来源记录 this.configSources = {}; // 配置服务器 this.server = this.initServerConfig(argv); // 配置飞书 this.feishu = this.initFeishuConfig(argv); // 配置日志 this.log = this.initLogConfig(argv); // 配置缓存 this.cache = this.initCacheConfig(argv); } /** * 获取配置单例 * @returns 配置实例 */ public static getInstance(): Config { if (!Config.instance) { Config.instance = new Config(); } return Config.instance; } /** * 解析命令行参数 * @returns 解析后的参数对象 */ private parseCommandLineArgs(): any { return yargs(hideBin(process.argv)) .options({ port: { type: 'number', description: '服务器监听端口' }, 'log-level': { type: 'string', description: '日志级别 (debug, info, log, warn, error, none)' }, 'feishu-app-id': { type: 'string', description: '飞书应用ID' }, 'feishu-app-secret': { type: 'string', description: '飞书应用密钥' }, 'feishu-base-url': { type: 'string', description: '飞书API基础URL' }, 'cache-enabled': { type: 'boolean', description: '是否启用缓存' }, 'cache-ttl': { type: 'number', description: '缓存生存时间(秒)' }, 'feishu-auth-type': { type: 'string', description: '飞书认证类型 (tenant 或 user)' }, 'feishu-token-endpoint': { type: 'string', description: '获取token的接口地址,默认 http://localhost:3333/getToken' } }) .help() .parseSync(); } /** * 初始化服务器配置 * @param argv 命令行参数 * @returns 服务器配置 */ private initServerConfig(argv: any): ServerConfig { const serverConfig: ServerConfig = { port: 3333, }; // 处理PORT if (argv.port) { serverConfig.port = argv.port; this.configSources['server.port'] = ConfigSource.CLI; } else if (process.env.PORT) { serverConfig.port = parseInt(process.env.PORT, 10); this.configSources['server.port'] = ConfigSource.ENV; } else { this.configSources['server.port'] = ConfigSource.DEFAULT; } return serverConfig; } /** * 初始化飞书配置 * @param argv 命令行参数 * @returns 飞书配置 */ private initFeishuConfig(argv: any): FeishuConfig { // 先初始化serverConfig以获取端口 const serverConfig = this.server || this.initServerConfig(argv); const feishuConfig: FeishuConfig = { appId: '', appSecret: '', baseUrl: 'https://open.feishu.cn/open-apis', authType: 'tenant', // 默认 tokenEndpoint: `http://127.0.0.1:${serverConfig.port}/getToken`, // 默认动态端口 }; // 处理App ID if (argv['feishu-app-id']) { feishuConfig.appId = argv['feishu-app-id']; this.configSources['feishu.appId'] = ConfigSource.CLI; } else if (process.env.FEISHU_APP_ID) { feishuConfig.appId = process.env.FEISHU_APP_ID; this.configSources['feishu.appId'] = ConfigSource.ENV; } // 处理App Secret if (argv['feishu-app-secret']) { feishuConfig.appSecret = argv['feishu-app-secret']; this.configSources['feishu.appSecret'] = ConfigSource.CLI; } else if (process.env.FEISHU_APP_SECRET) { feishuConfig.appSecret = process.env.FEISHU_APP_SECRET; this.configSources['feishu.appSecret'] = ConfigSource.ENV; } // 处理Base URL if (argv['feishu-base-url']) { feishuConfig.baseUrl = argv['feishu-base-url']; this.configSources['feishu.baseUrl'] = ConfigSource.CLI; } else if (process.env.FEISHU_BASE_URL) { feishuConfig.baseUrl = process.env.FEISHU_BASE_URL; this.configSources['feishu.baseUrl'] = ConfigSource.ENV; } else { this.configSources['feishu.baseUrl'] = ConfigSource.DEFAULT; } // 处理authType if (argv['feishu-auth-type']) { feishuConfig.authType = argv['feishu-auth-type'] === 'user' ? 'user' : 'tenant'; this.configSources['feishu.authType'] = ConfigSource.CLI; } else if (process.env.FEISHU_AUTH_TYPE) { feishuConfig.authType = process.env.FEISHU_AUTH_TYPE === 'user' ? 'user' : 'tenant'; this.configSources['feishu.authType'] = ConfigSource.ENV; } else { this.configSources['feishu.authType'] = ConfigSource.DEFAULT; } // 处理tokenEndpoint if (argv['feishu-token-endpoint']) { feishuConfig.tokenEndpoint = argv['feishu-token-endpoint']; this.configSources['feishu.tokenEndpoint'] = ConfigSource.CLI; } else if (process.env.FEISHU_TOKEN_ENDPOINT) { feishuConfig.tokenEndpoint = process.env.FEISHU_TOKEN_ENDPOINT; this.configSources['feishu.tokenEndpoint'] = ConfigSource.ENV; } else { this.configSources['feishu.tokenEndpoint'] = ConfigSource.DEFAULT; } return feishuConfig; } /** * 初始化日志配置 * @param argv 命令行参数 * @returns 日志配置 */ private initLogConfig(argv: any): LogConfig { const logConfig: LogConfig = { level: LogLevel.INFO, showTimestamp: true, showLevel: true, timestampFormat: 'yyyy-MM-dd HH:mm:ss.SSS' }; // 处理日志级别 if (argv['log-level']) { logConfig.level = this.getLogLevelFromString(argv['log-level']); this.configSources['log.level'] = ConfigSource.CLI; } else if (process.env.LOG_LEVEL) { logConfig.level = this.getLogLevelFromString(process.env.LOG_LEVEL); this.configSources['log.level'] = ConfigSource.ENV; } else { this.configSources['log.level'] = ConfigSource.DEFAULT; } // 处理时间戳显示 if (process.env.LOG_SHOW_TIMESTAMP) { logConfig.showTimestamp = process.env.LOG_SHOW_TIMESTAMP.toLowerCase() === 'true'; this.configSources['log.showTimestamp'] = ConfigSource.ENV; } else { this.configSources['log.showTimestamp'] = ConfigSource.DEFAULT; } // 处理级别显示 if (process.env.LOG_SHOW_LEVEL) { logConfig.showLevel = process.env.LOG_SHOW_LEVEL.toLowerCase() === 'true'; this.configSources['log.showLevel'] = ConfigSource.ENV; } else { this.configSources['log.showLevel'] = ConfigSource.DEFAULT; } // 处理时间戳格式 if (process.env.LOG_TIMESTAMP_FORMAT) { logConfig.timestampFormat = process.env.LOG_TIMESTAMP_FORMAT; this.configSources['log.timestampFormat'] = ConfigSource.ENV; } else { this.configSources['log.timestampFormat'] = ConfigSource.DEFAULT; } return logConfig; } /** * 初始化缓存配置 * @param argv 命令行参数 * @returns 缓存配置 */ private initCacheConfig(argv: any): CacheConfig { const cacheConfig: CacheConfig = { enabled: true, ttl: 300, // 5分钟,单位:秒 maxSize: 100 }; // 处理缓存启用 if (argv['cache-enabled'] !== undefined) { cacheConfig.enabled = argv['cache-enabled']; this.configSources['cache.enabled'] = ConfigSource.CLI; } else if (process.env.CACHE_ENABLED) { cacheConfig.enabled = process.env.CACHE_ENABLED.toLowerCase() === 'true'; this.configSources['cache.enabled'] = ConfigSource.ENV; } else { this.configSources['cache.enabled'] = ConfigSource.DEFAULT; } // 处理TTL if (argv['cache-ttl']) { cacheConfig.ttl = argv['cache-ttl']; this.configSources['cache.ttl'] = ConfigSource.CLI; } else if (process.env.CACHE_TTL) { cacheConfig.ttl = parseInt(process.env.CACHE_TTL, 10); this.configSources['cache.ttl'] = ConfigSource.ENV; } else { this.configSources['cache.ttl'] = ConfigSource.DEFAULT; } // 处理最大缓存大小 if (process.env.CACHE_MAX_SIZE) { cacheConfig.maxSize = parseInt(process.env.CACHE_MAX_SIZE, 10); this.configSources['cache.maxSize'] = ConfigSource.ENV; } else { this.configSources['cache.maxSize'] = ConfigSource.DEFAULT; } return cacheConfig; } /** * 从字符串获取日志级别 * @param levelStr 日志级别字符串 * @returns 日志级别枚举值 */ private getLogLevelFromString(levelStr: string): LogLevel { switch (levelStr.toLowerCase()) { case 'debug': return LogLevel.DEBUG; case 'info': return LogLevel.INFO; case 'log': return LogLevel.LOG; case 'warn': return LogLevel.WARN; case 'error': return LogLevel.ERROR; case 'none': return LogLevel.NONE; default: return LogLevel.INFO; } } /** * 打印当前配置信息 * @param isStdioMode 是否在stdio模式下 */ public printConfig(isStdioMode: boolean = false): void { if (isStdioMode) return; Logger.info('当前配置:'); Logger.info('服务器配置:'); Logger.info(`- 端口: ${this.server.port} (来源: ${this.configSources['server.port']})`); Logger.info('飞书配置:'); if (this.feishu.appId) { Logger.info(`- App ID: ${this.maskApiKey(this.feishu.appId)} (来源: ${this.configSources['feishu.appId']})`); } if (this.feishu.appSecret) { Logger.info(`- App Secret: ${this.maskApiKey(this.feishu.appSecret)} (来源: ${this.configSources['feishu.appSecret']})`); } Logger.info(`- API URL: ${this.feishu.baseUrl} (来源: ${this.configSources['feishu.baseUrl']})`); Logger.info(`- 认证类型: ${this.feishu.authType} (来源: ${this.configSources['feishu.authType']})`); Logger.info('日志配置:'); Logger.info(`- 日志级别: ${LogLevel[this.log.level]} (来源: ${this.configSources['log.level']})`); Logger.info(`- 显示时间戳: ${this.log.showTimestamp} (来源: ${this.configSources['log.showTimestamp']})`); Logger.info(`- 显示日志级别: ${this.log.showLevel} (来源: ${this.configSources['log.showLevel']})`); Logger.info('缓存配置:'); Logger.info(`- 启用缓存: ${this.cache.enabled} (来源: ${this.configSources['cache.enabled']})`); Logger.info(`- 缓存TTL: ${this.cache.ttl}秒 (来源: ${this.configSources['cache.ttl']})`); Logger.info(`- 最大缓存条目: ${this.cache.maxSize} (来源: ${this.configSources['cache.maxSize']})`); } /** * 掩盖API密钥 * @param key API密钥 * @returns 掩盖后的密钥字符串 */ private maskApiKey(key: string): string { if (!key || key.length <= 4) return '****'; return `${key.substring(0, 2)}****${key.substring(key.length - 2)}`; } /** * 验证配置是否完整有效 * @returns 是否验证成功 */ public validate(): boolean { // 验证服务器配置 if (!this.server.port || this.server.port <= 0) { Logger.error('无效的服务器端口配置'); return false; } // 验证飞书配置 if (!this.feishu.appId) { Logger.error('缺少飞书应用ID,请通过环境变量FEISHU_APP_ID或命令行参数--feishu-app-id提供'); return false; } if (!this.feishu.appSecret) { Logger.error('缺少飞书应用Secret,请通过环境变量FEISHU_APP_SECRET或命令行参数--feishu-app-secret提供'); return false; } return true; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cso1z/Feishu-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server