Skip to main content
Glama

MCP-Gateway

config_loader.py8.07 kB
import json import os from typing import Dict, List, Optional, Any, Union from mcp import StdioServerParameters from errors import ConfigurationError import logging logger = logging.getLogger(__name__) def _valid_str_list(data: Any, field_name: str, svr_name: str) -> List[str]: """辅助函数,验证数据是否为字符串列表。""" if not isinstance(data, list): raise ConfigurationError( f"服务器 '{svr_name}' 的 '{field_name}' 必须是一个字符串列表。") val_list: List[str] = [] for i, item in enumerate(data): if not isinstance(item, str): raise ConfigurationError( f"服务器 '{svr_name}' 的 '{field_name}' 列表的第 {i+1} 个元素必须是字符串。") val_list.append(item) return val_list def _valid_str_dict(data: Any, field_name: str, svr_name: str) -> Dict[str, str]: """辅助函数,验证数据是否为字符串到字符串的字典。""" if not isinstance(data, dict): raise ConfigurationError( f"服务器 '{svr_name}' 的 '{field_name}' 必须是一个 JSON 对象 (键值均为字符串的字典)。") val_dict: Dict[str, str] = {} for key, value in data.items(): if not isinstance(key, str): raise ConfigurationError( f"服务器 '{svr_name}' 的 '{field_name}' 字典的键必须是字符串。") if not isinstance(value, str): raise ConfigurationError( f"服务器 '{svr_name}' 的 '{field_name}' 字典的值 (键: '{key}') 必须是字符串。") val_dict[key] = value return val_dict def load_and_validate_config(cfg_fpath: str) -> Dict[str, Dict[str, Any]]: """ 加载并验证 JSON 配置文件。 返回一个字典,其中键是服务器名称,值是经过验证和处理的服务器配置。 """ logger.debug(f"开始加载配置文件: {cfg_fpath}") if not os.path.exists(cfg_fpath): logger.error(f"配置文件未找到: {cfg_fpath}") raise ConfigurationError(f"配置文件不存在: {cfg_fpath}") try: with open(cfg_fpath, 'r', encoding='utf-8') as f: raw_data = json.load(f) except json.JSONDecodeError as e_json: logger.error(f"无法解析 JSON 配置文件 '{cfg_fpath}': {e_json}", exc_info=True) raise ConfigurationError(f"无法解析 JSON 配置文件: {cfg_fpath}, 错误: {e_json}") except Exception as e_read: logger.error(f"读取配置文件 '{cfg_fpath}' 时发生意外错误: {e_read}", exc_info=True) raise ConfigurationError(f"读取配置文件时发生意外错误: {cfg_fpath}, 错误: {e_read}") if not isinstance(raw_data, dict): logger.error("配置文件顶层必须是一个 JSON 对象 (字典)。") raise ConfigurationError("配置文件顶层必须是一个 JSON 对象 (字典)。") validated_configs: Dict[str, Dict[str, Any]] = {} logger.debug(f"找到 {len(raw_data)} 个服务器配置条目进行验证。") for svr_name_raw, srv_conf_raw in raw_data.items(): if not isinstance(svr_name_raw, str) or not svr_name_raw.strip(): logger.warning( f"配置中发现无效的服务器名称键: '{svr_name_raw}' (将被忽略)。名称必须是非空字符串。") continue svr_name = svr_name_raw.strip() if not isinstance(srv_conf_raw, dict): logger.warning( f"服务器 '{svr_name}' 的配置必须是一个 JSON 对象,实际类型: {type(srv_conf_raw)} (将被忽略)。" ) continue srv_conf: Dict[str, Any] = srv_conf_raw svr_type = srv_conf.get("type") if not isinstance(svr_type, str) or svr_type not in ["stdio", "sse"]: logger.warning( f"服务器 '{svr_name}' 的 'type' 字段无效或缺失。必须是 'stdio' 或 'sse',得到: {svr_type} (将被忽略)。" ) continue logger.debug(f"正在验证服务器 '{svr_name}' (类型: {svr_type})") val_cfg_entry: Dict[str, Any] = {"type": svr_type} try: if svr_type == "stdio": cmd = srv_conf.get("command") if not isinstance(cmd, str) or not cmd.strip(): raise ConfigurationError( f"Stdio 服务器 '{svr_name}' 的 'command' 必须是一个非空字符串。") cmd_args: List[str] = [] if "args" in srv_conf: cmd_args = _valid_str_list(srv_conf["args"], "args", svr_name) cmd_env: Optional[Dict[str, str]] = None if "env" in srv_conf and srv_conf["env"] is not None: cmd_env = _valid_str_dict(srv_conf["env"], "env", svr_name) stdio_params = StdioServerParameters(command=cmd.strip(), args=cmd_args, env=cmd_env) val_cfg_entry["params"] = stdio_params elif svr_type == "sse": sse_url = srv_conf.get("url") if not isinstance(sse_url, str) or not sse_url.strip(): raise ConfigurationError( f"SSE 服务器 '{svr_name}' 的 'url' 必须是一个非空字符串。") sse_url = sse_url.strip() if not sse_url.startswith(("http://", "https://")): raise ConfigurationError( f"SSE 服务器 '{svr_name}' 的 'url' ('{sse_url}') 看起来不是一个有效的 HTTP/HTTPS URL。" ) val_cfg_entry["url"] = sse_url if "command" in srv_conf: sse_cmd = srv_conf.get("command") if not isinstance(sse_cmd, str) or not sse_cmd.strip(): raise ConfigurationError( f"SSE 服务器 '{svr_name}' 的 'command' (用于本地启动) 必须是一个非空字符串。" ) val_cfg_entry['command'] = sse_cmd.strip() sse_cmd_args: List[str] = [] if "args" in srv_conf: sse_cmd_args = _valid_str_list(srv_conf["args"], "args", svr_name) val_cfg_entry['args'] = sse_cmd_args sse_cmd_env: Optional[Dict[str, str]] = None if "env" in srv_conf and srv_conf["env"] is not None: sse_cmd_env = _valid_str_dict(srv_conf["env"], "env", svr_name) val_cfg_entry['env'] = sse_cmd_env validated_configs[svr_name] = val_cfg_entry logger.debug(f"服务器 '{svr_name}' 配置验证通过。") except ConfigurationError as e_svr_cfg: logger.error(f"服务器 '{svr_name}' 配置无效,已跳过: {e_svr_cfg}") except Exception as e_svr_unexpected: logger.error( f"处理服务器 '{svr_name}' 配置时发生意外错误,已跳过: {e_svr_unexpected}", exc_info=True) if not validated_configs and raw_data: logger.error("配置文件中所有服务器配置均无效。") raise ConfigurationError("配置文件中没有有效的服务器配置。") elif not validated_configs: logger.info(f"配置文件 '{cfg_fpath}' 为空或不包含任何服务器配置。") logger.info( f"配置文件 '{cfg_fpath}' 加载和验证完成。共处理 {len(validated_configs)} 个有效的服务器配置。") return validated_configs

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/trtyr/MCP-Gateway'

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