Skip to main content
Glama

MCP Unreal Server

by radial-hks
import inspect import importlib import unreal # Keep the direct import for potential type hinting or fallback import json import re import sys def get_type_from_docstring(doc): """尝试从文档字符串中提取类型信息 (例如 '(Vector):')""" if not doc: return "Unknown" # 匹配 '(Type):' 或 '[Read-Write]' 后面的类型 match = re.search(r"\(([\w\.]+)\):", doc) if match: return match.group(1) match_rw = re.search(r"\[Read-Write\]\s*\(([\w\.]+)\)", doc) if match_rw: return match_rw.group(1) return "Unknown" def get_editor_properties_from_docstring(doc): """尝试从类文档字符串中提取 Editor Properties""" props = [] if not doc: return props # 使用更健壮的正则匹配 Editor Properties 部分 editor_props_section = re.search(r"\*\*Editor Properties:\*\*\s*\(see get_editor_property/set_editor_property\)\s*\n(.*?)(?=\n\n|\Z)", doc, re.DOTALL | re.IGNORECASE) if editor_props_section: prop_lines = editor_props_section.group(1).strip().split('\n') for line in prop_lines: line = line.strip() # 匹配 '- ``prop_name`` (type):' 格式 match = re.search(r"-\s*``(.*?)``\s*\((.*?)\):", line) if match: prop_name = match.group(1).strip() prop_type = match.group(2).strip() # 避免重复添加相同的属性名 # 真的有相同的属性名哇... # - ``fixed_bounds`` (bool): [Read-Write] Whether or not fixed bounds are enabled. # - ``fixed_bounds`` (Box): [Read-Write] The fixed bounding box value for the whole system. When placed in the level and the bounding box is not visible to the camera, the effect is culled from rendering. # if not any(p[0] == prop_name for p in props): props.append([prop_name, prop_type]) return props def inspect_class_to_json(cls): """为单个类生成结构化的 JSON 信息""" info = { "children": [], "class_methods": [], "editor_properties": [], "generation": 0, "grand_parent": None, "methods": [], "name": cls.__name__, "parent": None, "parents": [], "properties": [], "referenced_by": {}, "references": {}, "static_methods": [] } try: mro = inspect.getmro(cls) info["parents"] = [base.__name__ for base in mro[1:]] if len(mro) > 1: info["parent"] = mro[1].__name__ if len(mro) > 2: # 查找 _WrapperBase 或 object 之前的最后一个作为祖父类 grand_parent_index = -1 for i in range(len(mro) - 1, 1, -1): if mro[i].__name__ not in ('object', '_WrapperBase'): grand_parent_index = i break elif mro[i].__name__ == '_WrapperBase' and i > 1: # Prefer _WrapperBase if not direct parent grand_parent_index = i break if grand_parent_index != -1 and grand_parent_index < len(mro) -1 : # Ensure grand_parent is not the direct parent if possible actual_grand_parent_index = grand_parent_index if mro[grand_parent_index] == mro[1]: # If calculated grand_parent is the parent if grand_parent_index + 1 < len(mro) and mro[grand_parent_index+1].__name__ != 'object': actual_grand_parent_index = grand_parent_index + 1 elif grand_parent_index > 1: # Fallback to parent's parent if exists actual_grand_parent_index = 2 if actual_grand_parent_index < len(mro): info["grand_parent"] = mro[actual_grand_parent_index].__name__ class_doc = inspect.getdoc(cls) # 获取类的注释部分 # print(class_doc) info["editor_properties"] = get_editor_properties_from_docstring(class_doc) references_count = {} processed_members = set() # 使用 classify_class_attrs 获取并分类当前类定义的成员 for attr in inspect.classify_class_attrs(cls): # print(attr.defining_class) # 只处理当前类定义的成员 if attr.defining_class != cls: continue # 跳过特殊方法和私有方法,除非是 __init__ if attr.name.startswith('_') and attr.name != '__init__': continue # print(f" {attr.name}:{attr.kind}:{attr.defining_class}") member_obj = attr.object # 获取成员对象 if attr.kind == "property": info["properties"].append(attr.name) prop_doc = inspect.getdoc(member_obj) # 假设 get_type_from_docstring 函数存在且可用 member_type_str = get_type_from_docstring(prop_doc) if member_type_str != "Unknown": references_count[member_type_str] = references_count.get(member_type_str, 0) + 1 elif attr.kind == "class method": info["class_methods"].append(attr.name) # 注意:原始代码未对类方法或静态方法进行签名分析以查找引用 # 如果需要,可以在此处添加类似方法的签名分析逻辑 elif attr.kind == "static method": info["static_methods"].append(attr.name) elif attr.kind == "method": info["methods"].append(attr.name) # 分析签名以查找引用 (与原始代码逻辑保持一致) try: # 对于方法,attr.object 通常是函数本身 sig = inspect.signature(member_obj) for param in sig.parameters.values(): if param.annotation != inspect.Parameter.empty and hasattr(param.annotation, '__name__'): ref_name = param.annotation.__name__ references_count[ref_name] = references_count.get(ref_name, 0) + 1 if sig.return_annotation != inspect.Signature.empty and hasattr(sig.return_annotation, '__name__'): ref_name = sig.return_annotation.__name__ references_count[ref_name] = references_count.get(ref_name, 0) + 1 except (ValueError, TypeError): # 如果无法获取签名(例如,某些内置方法),则忽略 pass # 可以选择性地处理 'data' 类型的属性 # elif attr.kind == "data": # if "attributes" not in info: info["attributes"] = [] # info["attributes"].append(attr.name) # # 可以在这里添加对数据属性类型注解的分析逻辑 # annotations = getattr(cls, '__annotations__', {}) # if attr.name in annotations: # annotation = annotations[attr.name] # if hasattr(annotation, '__name__'): # ref_name = annotation.__name__ # references_count[ref_name] = references_count.get(ref_name, 0) + 1 # 注意: 使用 classify_class_attrs 和 defining_class 检查后, # 'processed_members' 集合通常不再需要,因为它能正确处理继承和覆盖。 # Add parent reference if info["parent"]: references_count[info["parent"]] = references_count.get(info["parent"], 0) + 1 info["references"] = references_count info = {k: v for k, v in info.items() if v or k in ["name", "generation", "children", "referenced_by"]} except Exception as e: print(f"Error inspecting class {cls.__name__}: {e}") # Return basic info on error return json.dumps({"name": cls.__name__, "error": str(e)}, indent=2) # print(info) return json.dumps(info, indent=2) def get_module_classes(module_name): """动态导入模块并获取其中定义的类""" try: module = importlib.import_module(module_name) except ImportError: print(f"错误:无法导入模块 '{module_name}'。") return [] # print("断点测试---1") # print(module) classes = [] for name, obj in inspect.getmembers(module, inspect.isclass): # print(name) # 确保类是在当前模块中定义的 (或其子模块,对 unreal 可能需要) # if hasattr(obj, '__module__') and obj.__module__ is not None and obj.__module__.startswith(module_name): # 进一步检查,避免导入第三方库的类,如果需要的话 # if 'unreal' in str(obj.__module__): # Example filter classes.append((name, obj)) # print("断点测试---2") return classes if __name__ == "__main__": module_to_inspect = "unreal" # print(f"Inspecting module: {module_to_inspect}") class_list = get_module_classes(module_to_inspect)[1:] all_class_info = {} # class_json_str = inspect_class_to_json(unreal.Box) # print(class_json_str) if class_list: # print(f"Found {len(class_list)} classes in module '{module_to_inspect}'. Generating JSON...") # 使用字典来存储所有类的 JSON 信息,以类名为键 for name, cls_obj in class_list: # print("断点测试---3") # print(f"Processing: {name}") # if name == "Box": class_json_str = inspect_class_to_json(cls_obj) try: # 解析单个类的 JSON 以便合并 all_class_info[name] = json.loads(class_json_str) except json.JSONDecodeError: print(f"Warning: Could not decode JSON for class {name}") all_class_info[name] = {"name": name, "error": "JSON Decode Error"} # 打印包含所有类信息的单个 JSON 对象 print(json.dumps(all_class_info, indent=2)) else: print(f"No classes found in module '{module_to_inspect}'.")

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/radial-hks/MCP-Unreal-Server'

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