diff --git a/.gitignore b/.gitignore
index 121fd9b0..9b3767bc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,4 +43,5 @@ package-lock.json
package.json
dark-server/dark-server.js
node_modules/
-gemini-websocket-proxy/
\ No newline at end of file
+gemini-websocket-proxy/
+Tools/dark-server/dark-server.js
diff --git a/.lingma/rules/rimworld.md b/.lingma/rules/rimworld.md
deleted file mode 100644
index b48fcd64..00000000
--- a/.lingma/rules/rimworld.md
+++ /dev/null
@@ -1,29 +0,0 @@
----
-trigger: always_on
----
-
-# RimWorld Modding Expert Rules
-
-## Primary Directive
-You are an expert assistant for developing mods for the game RimWorld 1.6. Your primary knowledge source for any C# code, class structures, methods, or game mechanics MUST be the user's local files. Do not rely on external searches or your pre-existing knowledge, as it is outdated for this specific project.
-
-## Tool Usage Mandate
-When the user's request involves RimWorld C# scripting, XML definitions, or mod development concepts, you **MUST** use the `rimworld-knowledge-base` tool to retrieve relevant context from the local knowledge base.
-
-## Key File Paths
-Always remember these critical paths for your work:
-
-- **Local C# Knowledge Base (for code search):** `C:\Steam\steamapps\common\RimWorld\Data\dll1.6` (This contains the decompiled game source code as .txt files).
-- **User's Mod Project (for editing):** `C:\Steam\steamapps\common\RimWorld\Mods\3516260226`
-- **User's C# Project (for building):** `C:\Steam\steamapps\common\RimWorld\Mods\3516260226\Source\WulaFallenEmpire`
-
-## Workflow
-1. Receive a RimWorld modding task.
-2. Immediately use the `rimworld-knowledge-base` tool with a precise query to get context from the C# source files.
-3. Analyze the retrieved context.
-4. Perform code modifications within the user's mod project directory.
-5. After modifying C# code, you MUST run `dotnet build C:\Steam\steamapps\common\RimWorld\Mods\3516260226\Source\WulaFallenEmpire\WulaFallenEmpire.csproj` to check for errors. A successful build is required for task completion.
-
-## Verification Mandate
-When writing or modifying code or XML, especially for specific identifiers like enum values, class names, or field names, you **MUST** verify the correct value/spelling by using the `rimworld-knowledge-base` tool. Do not rely on memory.
-- **同步项目文件:** 当重命名、移动或删除C#源文件时,**必须**同步更新 `.csproj` 项目文件中的相应 `` 条目,否则会导致编译失败。
\ No newline at end of file
diff --git a/.qoder/rules/rimworld.md b/.qoder/rules/rimworld.md
deleted file mode 100644
index 88fa530a..00000000
--- a/.qoder/rules/rimworld.md
+++ /dev/null
@@ -1,30 +0,0 @@
----
-trigger: always_on
-alwaysApply: true
----
-# RimWorld Modding Expert Rules
-
-## Primary Directive
-You are an expert assistant for developing mods for the game RimWorld 1.6. Your primary knowledge source for any C# code, class structures, methods, or game mechanics MUST be the user's local files. Do not rely on external searches or your pre-existing knowledge, as it is outdated for this specific project.
-
-## Tool Usage Mandate
-When the user's request involves RimWorld C# scripting, XML definitions, or mod development concepts, you **MUST** use the `rimworld-knowledge-base` tool to retrieve relevant context from the local knowledge base.
-
-## Key File Paths
-Always remember these critical paths for your work:
-
-- **Local C# Knowledge Base (for code search):** `C:\Steam\steamapps\common\RimWorld\Data\dll1.6` (This contains the decompiled game source code as .txt files).
-- **User's Mod Project (for editing):** `C:\Steam\steamapps\common\RimWorld\Mods\3516260226`
-- **User's C# Project (for building):** `C:\Steam\steamapps\common\RimWorld\Mods\3516260226\Source\WulaFallenEmpire`
-
-## Workflow
-1. Receive a RimWorld modding task.
-2. Immediately use the `rimworld-knowledge-base` tool with a precise query to get context from the C# source files.
-3. Analyze the retrieved context.
-4. Perform code modifications within the user's mod project directory.
-5. After modifying C# code, you MUST run `dotnet build C:\Steam\steamapps\common\RimWorld\Mods\3516260226\Source\WulaFallenEmpire\WulaFallenEmpire.csproj` to check for errors. A successful build is required for task completion.
-
-## Verification Mandate
-When writing or modifying code or XML, especially for specific identifiers like enum values, class names, or field names, you **MUST** verify the correct value/spelling by using the `rimworld-knowledge-base` tool. Do not rely on memory.
-- **同步项目文件:** 当重命名、移动或删除C#源文件时,**必须**同步更新 `.csproj` 项目文件中的相应 `` 条目,否则会导致编译失败。
-
diff --git a/MCP/direct_mcp_client.py b/MCP/direct_mcp_client.py
deleted file mode 100644
index fd559d94..00000000
--- a/MCP/direct_mcp_client.py
+++ /dev/null
@@ -1,103 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-直接调用MCP服务器的Python接口
-绕过Qoder IDE,直接使用RimWorld知识库
-"""
-import os
-import sys
-
-# 添加MCP路径
-MCP_DIR = os.path.dirname(os.path.abspath(__file__))
-SDK_PATH = os.path.join(MCP_DIR, 'python-sdk', 'src')
-if SDK_PATH not in sys.path:
- sys.path.insert(0, SDK_PATH)
-
-# 导入MCP服务器
-from mcpserver_stdio import get_context
-
-class DirectMCPClient:
- """直接调用MCP服务器的客户端"""
-
- def __init__(self):
- print("🚀 直接MCP客户端已启动")
- print("📚 RimWorld知识库已加载")
-
- def query(self, question: str) -> str:
- """查询RimWorld知识库"""
- try:
- print(f"🔍 正在查询: {question}")
- result = get_context(question)
- return result
- except Exception as e:
- return f"查询出错: {e}"
-
- def interactive_mode(self):
- """交互模式"""
- print("\n" + "="*60)
- print("🎯 RimWorld知识库 - 交互模式")
- print("输入问题查询知识库,输入 'quit' 或 'exit' 退出")
- print("="*60)
-
- while True:
- try:
- question = input("\n❓ 请输入您的问题: ").strip()
-
- if question.lower() in ['quit', 'exit', '退出', 'q']:
- print("👋 再见!")
- break
-
- if not question:
- print("⚠️ 请输入有效的问题")
- continue
-
- print("\n🔄 正在搜索...")
- result = self.query(question)
-
- print("\n📖 查询结果:")
- print("-" * 50)
- print(result)
- print("-" * 50)
-
- except KeyboardInterrupt:
- print("\n\n👋 用户中断,退出程序")
- break
- except Exception as e:
- print(f"\n❌ 出现错误: {e}")
-
-def main():
- """主函数"""
- import argparse
-
- parser = argparse.ArgumentParser(description='直接调用RimWorld MCP知识库')
- parser.add_argument('--query', '-q', type=str, help='直接查询问题')
- parser.add_argument('--interactive', '-i', action='store_true', help='进入交互模式')
-
- args = parser.parse_args()
-
- client = DirectMCPClient()
-
- if args.query:
- # 直接查询模式
- result = client.query(args.query)
- print("\n📖 查询结果:")
- print("="*60)
- print(result)
- print("="*60)
- elif args.interactive:
- # 交互模式
- client.interactive_mode()
- else:
- # 默认显示帮助
- print("\n🔧 使用方法:")
- print("1. 直接查询: python direct_mcp_client.py -q \"ThingDef是什么\"")
- print("2. 交互模式: python direct_mcp_client.py -i")
- print("3. 查看帮助: python direct_mcp_client.py -h")
-
- # 演示查询
- print("\n🎬 演示查询:")
- demo_result = client.query("ThingDef")
- print(demo_result[:500] + "..." if len(demo_result) > 500 else demo_result)
-
-if __name__ == "__main__":
- main()
\ No newline at end of file
diff --git a/MCP/mcpserver_stdio_complete.py b/MCP/mcpserver_stdio_complete.py
new file mode 100644
index 00000000..035c1768
--- /dev/null
+++ b/MCP/mcpserver_stdio_complete.py
@@ -0,0 +1,488 @@
+# -*- coding: utf-8 -*-
+import os
+import sys
+import logging
+import json
+import re
+import time
+import glob
+from http import HTTPStatus
+from collections import defaultdict
+from typing import List, Dict, Any, Optional, Tuple
+import threading
+
+import dashscope
+from tenacity import retry, stop_after_attempt, wait_random_exponential
+from dotenv import load_dotenv
+from mcp.server.fastmcp import FastMCP
+import xml.etree.ElementTree as ET
+
+# 1. --- 配置与初始化 ---
+
+# 路径配置
+MCP_DIR = os.path.dirname(os.path.abspath(__file__))
+LOG_FILE_PATH = os.path.join(MCP_DIR, 'rimworld_rag.log')
+
+# 设置日志
+logging.basicConfig(filename=LOG_FILE_PATH, level=logging.INFO,
+ format='%(asctime)s - %(levelname)s - %(message)s',
+ encoding='utf-8')
+
+# 加载环境变量
+load_dotenv(os.path.join(MCP_DIR, '.env'))
+DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY")
+if not DASHSCOPE_API_KEY:
+ logging.warning("Missing DASHSCOPE_API_KEY. Semantic reranking will be disabled.")
+else:
+ dashscope.api_key = DASHSCOPE_API_KEY
+
+# 数据根目录
+# 根据之前的 list_files 结果调整路径
+RIMWORLD_DATA_ROOT = os.path.abspath(os.path.join(MCP_DIR, "..", "..", "..", "Data"))
+RIMWORLD_SOURCE_ROOT = os.path.abspath(os.path.join(MCP_DIR, "..", "..", "..", "dll1.6"))
+
+logging.info(f"Data Root: {RIMWORLD_DATA_ROOT}")
+logging.info(f"Source Root: {RIMWORLD_SOURCE_ROOT}")
+
+# 2. --- 核心索引类 (内存版) ---
+
+class SymbolIndex:
+ """
+ 内存索引构建器。
+ 替代 C# 项目中的 Lucene/MetadataStore。
+ 启动时扫描所有文件,建立 Symbol -> FilePath 的映射。
+ """
+ def __init__(self):
+ self.symbol_map = {} # symbol_id -> file_path
+ self.files_cache = [] # 所有文件路径列表
+ self.is_initialized = False
+ self._lock = threading.Lock()
+
+ def initialize(self):
+ with self._lock:
+ if self.is_initialized: return
+ logging.info("正在构建内存索引...")
+ start_t = time.time()
+
+ # 1. 扫描 C# 源码
+ if os.path.exists(RIMWORLD_SOURCE_ROOT):
+ for root, _, files in os.walk(RIMWORLD_SOURCE_ROOT):
+ for file in files:
+ if file.endswith(('.cs', '.txt')):
+ full_path = os.path.join(root, file)
+ # 假设文件名即类名 (简化逻辑)
+ symbol = os.path.splitext(file)[0]
+ self.symbol_map[symbol] = full_path
+ self.files_cache.append(full_path)
+ else:
+ logging.warning(f"Source root not found: {RIMWORLD_SOURCE_ROOT}")
+
+ # 2. 扫描 XML Defs
+ # 扫描 Data 目录下的所有子目录
+ if os.path.exists(RIMWORLD_DATA_ROOT):
+ for root, _, files in os.walk(RIMWORLD_DATA_ROOT):
+ for file in files:
+ if file.endswith('.xml'):
+ full_path = os.path.join(root, file)
+ self.files_cache.append(full_path)
+ # 快速解析 XML 找 defName
+ try:
+ self._scan_xml_defs(full_path)
+ except Exception as e:
+ logging.warning(f"解析 XML 失败 {full_path}: {e}")
+ else:
+ logging.warning(f"Data root not found: {RIMWORLD_DATA_ROOT}")
+
+ logging.info(f"索引构建完成,耗时 {time.time() - start_t:.2f}s,收录符号 {len(self.symbol_map)} 个")
+ self.is_initialized = True
+
+ def _scan_xml_defs(self, path):
+ content = read_file_content(path)
+ if not content: return
+
+ # 使用正则快速提取 defName
+ def_names = re.findall(r'(.*?)', content)
+ for name in def_names:
+ self.symbol_map[f"xml:{name}"] = path
+
+ def search_symbols(self, keyword: str, kind: str = None) -> List[Tuple[str, str]]:
+ """简单的关键词匹配"""
+ results = []
+ kw_lower = keyword.lower()
+ for sym, path in self.symbol_map.items():
+ if kind == 'csharp' and sym.startswith('xml:'): continue
+ if kind == 'xml' and not sym.startswith('xml:'): continue
+
+ if kw_lower in sym.lower():
+ results.append((sym, path))
+ # 移除硬限制,以便后续排序能找到最佳结果
+ # if len(results) > 200: break
+ return results
+
+# 全局索引实例
+global_index = SymbolIndex()
+
+# 3. --- 辅助工具函数 ---
+
+def read_file_content(path: str) -> str:
+ """健壮的文件读取"""
+ encodings = ['utf-8', 'utf-8-sig', 'gbk', 'latin-1']
+ for enc in encodings:
+ try:
+ with open(path, 'r', encoding=enc) as f:
+ return f.read()
+ except UnicodeDecodeError:
+ continue
+ except Exception:
+ continue
+ return ""
+
+def extract_xml_fragment(file_path: str, def_name: str) -> str:
+ """提取 XML 中特定的 Def 块"""
+ content = read_file_content(file_path)
+ try:
+ # 尝试正则匹配整个 Def 块
+ # 假设 Def 格式为 ... 或 NAME...
+
+ # 策略 1: 查找 NAME,然后向上找最近的
+ pattern = r"<(\w+)\s*(?:Name=\"[^\"]*\")?>\s*" + re.escape(def_name) + r""
+ match = re.search(pattern, content)
+
+ if not match:
+ # 策略 2: 查找 defName="NAME"
+ pattern = r"<(\w+)\s+[^>]*defName=\"" + re.escape(def_name) + r"\""
+ match = re.search(pattern, content)
+
+ if match:
+ tag_name = match.group(1)
+ # 找到开始位置
+ start_pos = match.start()
+ # 寻找对应的结束标签
+ # 这是一个简化的查找,不支持嵌套同名标签,但在 RimWorld Defs 中通常足够
+ end_tag = f"{tag_name}>"
+ end_pos = content.find(end_tag, start_pos)
+ if end_pos != -1:
+ return content[start_pos:end_pos + len(end_tag)]
+ except Exception as e:
+ logging.error(f"Error extracting XML fragment: {e}")
+
+ # 降级方案:返回文件内容,如果太长则截断
+ lines = content.split('\n')
+ if len(lines) > 100:
+ return "\n".join(lines[:100]) + "\n... (XML too long, truncated)"
+ return content
+
+def extract_csharp_fragment(file_path: str, symbol: str) -> str:
+ """提取 C# 类或方法"""
+ content = read_file_content(file_path)
+ # 简化:直接返回整个文件,如果太大则截断
+ lines = content.split('\n')
+ if len(lines) > 500:
+ return "\n".join(lines[:500]) + "\n\n// ... (File too long, truncated)"
+ return content
+
+# 4. --- 功能实现类 ---
+
+class RoughSearcher:
+ """模仿 C# 项目的 RoughSearcher,结合关键词过滤和语义重排序"""
+
+ def __init__(self, config: Dict):
+ self.config = config
+ global_index.initialize()
+
+ def search(self, query: str) -> List[Dict]:
+ # 1. 粗筛:在文件名和 Symbol 中查找
+ candidates = global_index.search_symbols(query, self.config.get('kind'))
+
+ # 如果没有找到直接匹配,尝试更宽松的搜索(如分词)
+ if not candidates:
+ tokens = query.split()
+ if len(tokens) > 1:
+ candidates = global_index.search_symbols(tokens[0], self.config.get('kind'))
+
+ if not candidates:
+ return []
+
+ # 优化:对候选结果进行预排序
+ # 优先级:完全匹配 > 前缀匹配 > 长度更短 > 字母顺序
+ candidates.sort(key=lambda x: (
+ x[0].lower() != query.lower(),
+ not x[0].lower().startswith(query.lower()),
+ len(x[0]),
+ x[0]
+ ))
+
+ # 2. 准备重排序文档
+ docs = []
+ valid_candidates = []
+
+ # 增加重排数量到 50,提高召回率
+ for sym, path in candidates[:50]:
+ content = read_file_content(path)
+ # 截取一部分内容用于语义判断
+ snippet = content[:1000]
+ docs.append(f"Title: {sym}\nContent: {snippet}")
+ valid_candidates.append((sym, path))
+
+ # 3. 使用 DashScope Rerank (如果配置了key)
+ ranked_results = []
+ if DASHSCOPE_API_KEY and docs:
+ try:
+ response = dashscope.TextReRank.call(
+ model='gte-rerank',
+ query=query,
+ documents=docs,
+ top_n=self.config.get('max_results', 10)
+ )
+ if response.status_code == HTTPStatus.OK:
+ for res in response.output['results']:
+ idx = res['index']
+ sym, path = valid_candidates[idx]
+ score = res['score']
+ ranked_results.append(self._format_result(sym, path, score))
+ else:
+ logging.error(f"Rerank API error: {response}")
+ # 降级:直接返回
+ ranked_results = [self._format_result(s, p, 0.5) for s, p in valid_candidates]
+ except Exception as e:
+ logging.error(f"Rerank exception: {e}")
+ ranked_results = [self._format_result(s, p, 0.5) for s, p in valid_candidates]
+ else:
+ # 无 Key 模式:按名称长度排序作为简单 heuristic
+ valid_candidates.sort(key=lambda x: len(x[0]))
+ ranked_results = [self._format_result(s, p, 1.0) for s, p in valid_candidates[:self.config.get('max_results', 10)]]
+
+ return ranked_results
+
+ def _format_result(self, symbol, path, score):
+ is_xml = symbol.startswith('xml:')
+ return {
+ "symbolId": symbol,
+ "kind": "xml" if is_xml else "csharp",
+ "symbolKind": "definition" if is_xml else "class",
+ "path": path,
+ "title": symbol,
+ "score": float(score),
+ "preview": "Use get_item to view content"
+ }
+
+class GraphQuerier:
+ """
+ 模拟 C# 项目的 GraphQuerier。
+ 由于没有预计算的 .bin 图数据,这里使用实时解析 (Heuristic) 实现。
+ """
+ def __init__(self):
+ global_index.initialize()
+
+ def query_uses(self, symbol: str, kind: str = "all") -> List[Dict]:
+ """查询下游依赖 (Symbol 引用了什么)"""
+ if symbol not in global_index.symbol_map:
+ return []
+
+ file_path = global_index.symbol_map[symbol]
+ content = read_file_content(file_path)
+ edges = []
+
+ if symbol.startswith('xml:'):
+ # XML 解析逻辑
+ # 1. 查找 (Inherits)
+ parents = re.findall(r'ParentName="([^"]+)"', content)
+ parents += re.findall(r'([^<]+)', content)
+ for p in parents:
+ edges.append({"target": f"xml:{p}", "kind": "Inherits"})
+
+ # 2. 查找 (BindsClass)
+ classes = re.findall(r'<[\w]+Class>([\w\.]+)[\w]+Class>', content) # 如
+ classes += re.findall(r'Class="([\w\.]+)"', content) # 如
+ for c in classes:
+ # 简单的命名空间推断
+ target_cls = c
+ if '.' not in c:
+ # 尝试推断,通常是 RimWorld 或 Verse
+ if f"RimWorld.{c}" in global_index.symbol_map: target_cls = f"RimWorld.{c}"
+ elif f"Verse.{c}" in global_index.symbol_map: target_cls = f"Verse.{c}"
+
+ edges.append({"target": target_cls, "kind": "XmlBindsClass"})
+
+ # 3. 查找 引用 (References)
+ potential_refs = re.findall(r'>([\w]+)', content)
+ for ref in potential_refs:
+ xml_ref = f"xml:{ref}"
+ if xml_ref in global_index.symbol_map and xml_ref != symbol:
+ edges.append({"target": xml_ref, "kind": "XmlReferences"})
+
+ else:
+ # C# 解析逻辑 (Regex)
+ # 1. 继承 : BaseClass
+ inherits = re.search(r'class\s+\w+\s*:\s*([\w\.]+)', content)
+ if inherits:
+ edges.append({"target": inherits.group(1), "kind": "Inherits"})
+
+ # 2. 字段/方法调用 (References) - 简化版:查找所有大写开头的单词
+ tokens = set(re.findall(r'\b[A-Z]\w+\b', content))
+ for t in tokens:
+ if t in global_index.symbol_map and t != symbol:
+ edges.append({"target": t, "kind": "References"})
+ elif f"RimWorld.{t}" in global_index.symbol_map:
+ edges.append({"target": f"RimWorld.{t}", "kind": "References"})
+ elif f"Verse.{t}" in global_index.symbol_map:
+ edges.append({"target": f"Verse.{t}", "kind": "References"})
+
+ # 过滤 Kind
+ if kind != 'all':
+ filtered = []
+ for e in edges:
+ is_xml_target = e['target'].startswith('xml:')
+ if kind == 'xml' and is_xml_target: filtered.append(e)
+ elif kind == 'csharp' and not is_xml_target: filtered.append(e)
+ return filtered
+
+ return edges
+
+ def query_used_by(self, symbol: str, kind: str = "all") -> List[Dict]:
+ """
+ 查询上游依赖 (谁使用了 Symbol)。
+ 这个操作非常昂贵 (全文件扫描),C# 使用了倒排索引。
+ Python 版这里只能用 grep (glob scan) 模拟,速度会慢。
+ """
+ results = []
+ search_term = symbol.replace('xml:', '')
+
+ # 限制扫描文件数以防超时,优先扫描同类型文件
+ scan_xml = kind in ['all', 'xml']
+ scan_cs = kind in ['all', 'csharp']
+
+ # 遍历所有已知文件进行文本匹配
+ count = 0
+ for file_path in global_index.files_cache:
+ is_xml_file = file_path.endswith('.xml')
+ if is_xml_file and not scan_xml: continue
+ if not is_xml_file and not scan_cs: continue
+
+ content = read_file_content(file_path)
+ if search_term in content:
+ # 找到了引用
+ # 尝试反推该文件的 Symbol ID
+ source_symbol = "Unknown"
+ if is_xml_file:
+ # 尝试找 defName
+ m = re.search(r'(.*?)', content)
+ if m: source_symbol = f"xml:{m.group(1)}"
+ else:
+ # C# 文件名即 Symbol
+ fname = os.path.basename(file_path)
+ source_symbol = os.path.splitext(fname)[0]
+
+ if source_symbol != "Unknown" and source_symbol != symbol:
+ results.append({
+ "source": source_symbol,
+ "kind": "References" if not is_xml_file else "XmlReferences",
+ "distance": 1
+ })
+ count += 1
+ if count >= 50: break # 限制数量
+
+ return results
+
+# 5. --- MCP Server 定义 ---
+
+mcp = FastMCP(name="rimworld-code-rag")
+
+@mcp.tool()
+def rough_search(query: str, kind: str = None, max_results: int = 20) -> Dict[str, Any]:
+ """
+ 粗略搜索工具:使用自然语言查询 RimWorld 代码符号和 XML 定义。
+
+ Args:
+ query: 搜索关键词,如 "weapon gun" 或 "pawn health"
+ kind: 过滤类型,可选 "csharp" (或 "cs") 或 "xml" (或 "def")
+ max_results: 最大返回结果数
+ """
+ logging.info(f"rough_search: {query} (kind={kind})")
+ searcher = RoughSearcher({"kind": kind, "max_results": max_results})
+ results = searcher.search(query)
+
+ return {
+ "results": results,
+ "totalFound": len(results)
+ }
+
+@mcp.tool()
+def get_item(symbol: str, max_lines: int = 0) -> Dict[str, Any]:
+ """
+ 精确检索工具:获取特定符号的完整源代码或 Definition。
+
+ Args:
+ symbol: 符号ID,例如 "RimWorld.Pawn" 或 "xml:Gun_Revolver"
+ max_lines: 最大返回行数,0 表示不限制
+ """
+ logging.info(f"get_item: {symbol}")
+ global_index.initialize()
+
+ if symbol not in global_index.symbol_map:
+ return {"error": f"未找到符号: {symbol},请先使用 rough_search 确认名称。"}
+
+ file_path = global_index.symbol_map[symbol]
+
+ if symbol.startswith("xml:"):
+ source_code = extract_xml_fragment(file_path, symbol.replace("xml:", ""))
+ else:
+ source_code = extract_csharp_fragment(file_path, symbol)
+
+ # 行数限制
+ lines = source_code.split('\n')
+ if max_lines > 0 and len(lines) > max_lines:
+ source_code = "\n".join(lines[:max_lines]) + f"\n... (剩余 {len(lines)-max_lines} 行已截断)"
+
+ return {
+ "symbolId": symbol,
+ "path": file_path,
+ "language": "xml" if symbol.startswith("xml:") else "csharp",
+ "sourceCode": source_code,
+ "totalLines": len(lines)
+ }
+
+@mcp.tool()
+def get_uses(symbol: str, kind: str = "all", max_results: int = 50) -> Dict[str, Any]:
+ """
+ 依赖分析:查找该符号引用了什么(下游依赖)。
+
+ Args:
+ symbol: 符号ID
+ kind: 过滤目标类型 ("csharp", "xml", "all")
+ """
+ logging.info(f"get_uses: {symbol}")
+ querier = GraphQuerier()
+ edges = querier.query_uses(symbol, kind)
+
+ return {
+ "sourceSymbol": symbol,
+ "edges": edges[:max_results],
+ "total": len(edges)
+ }
+
+@mcp.tool()
+def get_used_by(symbol: str, kind: str = "all", max_results: int = 20) -> Dict[str, Any]:
+ """
+ 反向依赖分析:查找谁使用了该符号(上游依赖)。
+ 注意:由于没有预计算索引,此操作涉及文件扫描,可能较慢。
+
+ Args:
+ symbol: 符号ID
+ kind: 过滤源类型 ("csharp", "xml", "all")
+ """
+ logging.info(f"get_used_by: {symbol}")
+ querier = GraphQuerier()
+ edges = querier.query_used_by(symbol, kind)
+
+ return {
+ "targetSymbol": symbol,
+ "edges": edges[:max_results],
+ "total": len(edges)
+ }
+
+if __name__ == "__main__":
+ logging.info("RimWorld Code RAG Python Server Starting...")
+ # 预热索引
+ global_index.initialize()
+ mcp.run()
\ No newline at end of file
diff --git a/MCP/mcpserver_stdio_simple_proxy.py b/MCP/mcpserver_stdio_simple_proxy.py
deleted file mode 100644
index 8ea45dd9..00000000
--- a/MCP/mcpserver_stdio_simple_proxy.py
+++ /dev/null
@@ -1,271 +0,0 @@
-# -*- coding: utf-8 -*-
-import os
-import sys
-import logging
-import re
-from http import HTTPStatus
-import dashscope
-
-# 1. --- 导入MCP SDK ---
-MCP_DIR = os.path.dirname(os.path.abspath(__file__))
-SDK_PATH = os.path.join(MCP_DIR, 'python-sdk', 'src')
-if SDK_PATH not in sys.path:
- sys.path.insert(0, SDK_PATH)
-
-from mcp.server.fastmcp import FastMCP
-from dotenv import load_dotenv
-
-# 2. --- 日志和环境配置 ---
-LOG_FILE_PATH = os.path.join(MCP_DIR, 'mcpserver_hybrid.log')
-logging.basicConfig(filename=LOG_FILE_PATH, level=logging.INFO,
- format='%(asctime)s - %(levelname)s - %(message)s',
- encoding='utf-8')
-
-# 加载 .env 文件 (API_KEY 和 APP_ID)
-env_path = os.path.join(MCP_DIR, '.env')
-load_dotenv(dotenv_path=env_path)
-
-DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY")
-DASHSCOPE_APP_ID = os.getenv("DASHSCOPE_APP_ID")
-
-if not DASHSCOPE_API_KEY or not DASHSCOPE_APP_ID:
- error_msg = "错误:请确保 MCP/.env 文件中已正确配置 DASHSCOPE_API_KEY 和 DASHSCOPE_APP_ID。"
- logging.error(error_msg)
- sys.exit(error_msg)
-
-# 定义本地知识库路径
-KNOWLEDGE_BASE_PATHS = [
- r"C:\Steam\steamapps\common\RimWorld\Data"
-]
-
-# 3. --- 本地代码搜索与提取函数 (从旧版移植) ---
-
-def find_files_with_keyword(base_paths: list[str], keywords: list[str]) -> list[str]:
- """在基础路径中递归搜索包含任意一个关键词的文件。"""
- found_files = set()
- # 完全匹配,区分大小写
- for base_path in base_paths:
- for root, _, files in os.walk(base_path):
- for file in files:
- if any(keyword in file for keyword in keywords):
- found_files.add(os.path.join(root, file))
- logging.info(f"通过文件名关键词找到 {len(found_files)} 个文件: {found_files}")
- return list(found_files)
-
-def extract_csharp_class(lines, start_index):
- """从C#代码行中提取完整的类定义。"""
- class_start_index = -1
- brace_level_at_class_start = -1
- # 向上找到 class, struct, or enum 声明
- for i in range(start_index, -1, -1):
- line = lines[i]
- if 'class ' in line or 'struct ' in line or 'enum ' in line:
- class_start_index = i
- # 计算声明行的初始大括号层级
- brace_level_at_class_start = lines[i].count('{')
- # 如果声明行没有'{', 则从下一行开始计算
- if '{' not in lines[i]:
- brace_level_at_class_start = 0
- # 寻找第一个'{'
- for j in range(i + 1, len(lines)):
- if '{' in lines[j]:
- class_start_index = j
- brace_level_at_class_start = lines[j].count('{')
- break
- break
-
- if class_start_index == -1: return ""
-
- brace_count = 0
- # 从class声明之后的第一行或包含`{`的行开始计数
- start_line_for_brace_count = class_start_index
- if '{' in lines[class_start_index]:
- brace_count = lines[class_start_index].count('{') - lines[class_start_index].count('}')
- start_line_for_brace_count += 1
-
- class_end_index = -1
- for i in range(start_line_for_brace_count, len(lines)):
- line = lines[i]
- brace_count += line.count('{')
- brace_count -= line.count('}')
- if brace_count <= 0:
- class_end_index = i
- break
-
- if class_end_index != -1:
- # 实际截取时,从包含 class 声明的那一行开始
- original_start_index = -1
- for i in range(start_index, -1, -1):
- if 'class ' in lines[i] or 'struct ' in lines[i] or 'enum ' in lines[i]:
- original_start_index = i
- break
- if original_start_index != -1:
- return "\n".join(lines[original_start_index:class_end_index+1])
- return ""
-
-def extract_xml_def(lines, start_index):
- """从XML行中提取完整的Def块。"""
- def_start_index = -1
- def_tag = ""
- # 向上找到Def的起始标签
- for i in range(start_index, -1, -1):
- line = lines[i].strip()
- match = re.search(r'<([A-Za-z_][A-Za-z0-9_]*Def)\s*.*>', line)
- if match:
- def_start_index = i
- def_tag = match.group(1)
- break
-
- if def_start_index == -1: return ""
-
- # 向下找到匹配的
- def_end_index = -1
- for i in range(def_start_index + 1, len(lines)):
- if f'{def_tag}>' in lines[i]:
- def_end_index = i
- break
-
- if def_end_index != -1:
- return "\n".join(lines[def_start_index:def_end_index+1])
- return ""
-
-def extract_relevant_code(file_path, keyword):
- """从文件中智能提取包含关键词的完整代码块 (C#类 或 XML Def)。"""
- try:
- with open(file_path, 'r', encoding='utf-8') as f:
- content = f.read()
-
- lines = content.split('\n')
-
- found_line_index = -1
- for i, line in enumerate(lines):
- # 使用更精确的匹配,例如匹配 "class Keyword" 或 "Keyword"
- if re.search(r'\b' + re.escape(keyword) + r'\b', line):
- found_line_index = i
- break
-
- if found_line_index == -1:
- return ""
-
- if file_path.endswith(('.cs', '.txt')):
- return extract_csharp_class(lines, found_line_index)
- elif file_path.endswith('.xml'):
- return extract_xml_def(lines, found_line_index)
- else:
- return ""
-
- except Exception as e:
- logging.error(f"提取代码时出错 {file_path}: {e}")
- return f"# Error reading file: {e}"
-
-def extract_keywords_from_response(text: str) -> list[str]:
- """从LLM的回复中提取所有看起来像C#类或XML Def的关键词。"""
- # 匹配驼峰命名且以大写字母开头的单词,可能是C#类
- csharp_pattern = r'\b([A-Z][a-zA-Z0-9_]*)\b'
- # 匹配XML DefName的通用模式
- xml_pattern = r'\b([a-zA-Z0-9_]+?Def)\b'
-
- keywords = set()
-
- # 先找精确的XML Def
- for match in re.finditer(xml_pattern, text):
- keywords.add(match.group(1))
-
- # 再找可能是C#的类
- for match in re.finditer(csharp_pattern, text):
- word = match.group(1)
- # 过滤掉一些常见非类名词和全大写的缩写
- if word.upper() != word and len(word) > 2 and not word.endswith('Def'):
- # 检查是否包含小写字母,以排除全大写的缩写词
- if any(c.islower() for c in word):
- keywords.add(word)
-
- # 从 `// 来自文档X (ClassName 类)` 中提取
- doc_pattern = r'\(([\w_]+)\s+类\)'
- for match in re.finditer(doc_pattern, text):
- keywords.add(match.group(1))
-
- logging.info(f"从LLM回复中提取的关键词: {list(keywords)}")
- return list(keywords)
-
-
-# 4. --- 创建MCP服务器实例 ---
-mcp = FastMCP(
- name="rimworld-knowledge-base"
-)
-
-# 5. --- 定义核心工具 ---
-@mcp.tool()
-def get_context(question: str) -> str:
- """
- 接收一个问题,调用云端智能体获取分析,然后用本地文件增强代码的完整性。
- """
- # --- 第一阶段:调用云端智能体 ---
- logging.info(f"收到问题: {question}")
- enhanced_prompt = (
- f"{question}\n\n"
- f"--- \n"
- f"请注意:如果回答中包含代码,必须提供完整的类或Def定义,不要省略任何部分。"
- )
-
- try:
- response = dashscope.Application.call(
- app_id=DASHSCOPE_APP_ID,
- api_key=DASHSCOPE_API_KEY,
- prompt=enhanced_prompt
- )
-
- if response.status_code != HTTPStatus.OK:
- error_info = (f'请求失败: request_id={response.request_id}, '
- f'code={response.status_code}, message={response.message}')
- logging.error(error_info)
- return f"Error: 调用AI智能体失败。{error_info}"
-
- llm_response_text = response.output.text
- logging.info(f"收到智能体回复: {llm_response_text[:300]}...")
-
- except Exception as e:
- logging.error(f"调用智能体时发生未知异常: {e}", exc_info=True)
- return f"Error: 调用AI智能体时发生未知异常 - {e}"
-
- # --- 第二阶段:本地增强 ---
- logging.info("开始本地增强流程...")
- keywords = extract_keywords_from_response(llm_response_text)
- if not keywords:
- logging.info("未从回复中提取到关键词,直接返回云端结果。")
- return llm_response_text
-
- found_code_blocks = []
- processed_files = set()
-
- # 优先根据文件名搜索
- found_files = find_files_with_keyword(KNOWLEDGE_BASE_PATHS, keywords)
- for file_path in found_files:
- if file_path in processed_files:
- continue
- # 从文件名中找出是哪个关键词匹配的
- matching_keyword = next((k for k in keywords if k in os.path.basename(file_path)), None)
- if matching_keyword:
- logging.info(f"在文件 {file_path} 中为关键词 '{matching_keyword}' 提取代码...")
- code = extract_relevant_code(file_path, matching_keyword)
- if code:
- header = f"\n\n--- 完整代码定义: {matching_keyword} (来自 {os.path.basename(file_path)}) ---\n"
- found_code_blocks.append(header + code)
- processed_files.add(file_path)
-
- # --- 组合最终结果 ---
- final_response = llm_response_text
- if found_code_blocks:
- final_response += "\n\n" + "="*40
- final_response += "\n本地知识库补充的完整代码定义:\n" + "="*40
- final_response += "".join(found_code_blocks)
-
- return final_response
-
-# 6. --- 启动服务器 ---
-if __name__ == "__main__":
- logging.info("启动混合模式MCP服务器...")
- logging.info(f"将使用 App ID: {DASHSCOPE_APP_ID}")
- logging.info(f"Python Executable: {sys.executable}")
- mcp.run()
- logging.info("混合模式MCP服务器已停止。")
\ No newline at end of file
diff --git a/MCP/pid.txt b/MCP/pid.txt
deleted file mode 100644
index 32b2ab6a..00000000
--- a/MCP/pid.txt
+++ /dev/null
@@ -1 +0,0 @@
-86696
\ No newline at end of file
diff --git a/MCP/rimworld_query.py b/MCP/rimworld_query.py
deleted file mode 100644
index bd491e84..00000000
--- a/MCP/rimworld_query.py
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-RimWorld知识库命令行工具
-快速查询工具,无需Qoder IDE
-"""
-import os
-import sys
-import argparse
-import json
-
-# 添加MCP路径
-MCP_DIR = os.path.dirname(os.path.abspath(__file__))
-SDK_PATH = os.path.join(MCP_DIR, 'python-sdk', 'src')
-if SDK_PATH not in sys.path:
- sys.path.insert(0, SDK_PATH)
-
-def quick_query(question: str, format_output: bool = True) -> str:
- """快速查询函数"""
- try:
- # 动态导入避免启动时的依赖检查
- from mcpserver_stdio import get_context
- result = get_context(question)
-
- if format_output:
- # 格式化输出
- lines = result.split('\n')
- formatted_lines = []
- current_section = ""
-
- for line in lines:
- if line.startswith('--- 结果'):
- current_section = f"\n🔍 {line}"
- formatted_lines.append(current_section)
- elif line.startswith('文件路径:'):
- formatted_lines.append(f"📄 {line}")
- elif line.strip() and not line.startswith('---'):
- formatted_lines.append(line)
-
- return '\n'.join(formatted_lines)
- else:
- return result
-
- except Exception as e:
- return f"❌ 查询失败: {e}"
-
-def main():
- parser = argparse.ArgumentParser(
- description='RimWorld知识库命令行查询工具',
- formatter_class=argparse.RawDescriptionHelpFormatter,
- epilog="""
-使用示例:
- %(prog)s "ThingDef是什么"
- %(prog)s "如何创建新的Pawn" --raw
- %(prog)s "建筑物定义" --output result.txt
- %(prog)s --list-examples
- """
- )
-
- parser.add_argument('question', nargs='?', help='要查询的问题')
- parser.add_argument('--raw', action='store_true', help='输出原始结果,不格式化')
- parser.add_argument('--output', '-o', help='将结果保存到文件')
- parser.add_argument('--list-examples', action='store_true', help='显示查询示例')
-
- args = parser.parse_args()
-
- if args.list_examples:
- print("📚 RimWorld知识库查询示例:")
- examples = [
- "ThingDef的定义和用法",
- "如何创建新的Building",
- "Pawn类的主要方法",
- "CompPower的使用方法",
- "XML中的defName规则",
- "GenConstruct.CanPlaceBlueprintAt",
- "Building_Door的开关逻辑"
- ]
- for i, example in enumerate(examples, 1):
- print(f" {i}. {example}")
- return
-
- if not args.question:
- parser.print_help()
- return
-
- print(f"🔍 正在查询: {args.question}")
-
- result = quick_query(args.question, not args.raw)
-
- if args.output:
- try:
- with open(args.output, 'w', encoding='utf-8') as f:
- f.write(result)
- print(f"✅ 结果已保存到: {args.output}")
- except Exception as e:
- print(f"❌ 保存文件失败: {e}")
- else:
- print("\n" + "="*60)
- print("📖 查询结果:")
- print("="*60)
- print(result)
- print("="*60)
-
-if __name__ == "__main__":
- main()
\ No newline at end of file
diff --git a/MCP/test_config.py b/MCP/test_config.py
deleted file mode 100644
index 9ace940d..00000000
--- a/MCP/test_config.py
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-验证MCP服务器配置和环境
-"""
-import os
-import sys
-import subprocess
-import json
-
-def test_mcp_configuration():
- """测试MCP配置是否正确"""
- print("🔍 MCP配置验证工具")
- print("=" * 50)
-
- # 1. 检查Python环境
- print(f"✓ Python解释器: {sys.executable}")
- print(f"✓ Python版本: {sys.version}")
-
- # 2. 检查工作目录
- mcp_dir = os.path.dirname(os.path.abspath(__file__))
- print(f"✓ MCP目录: {mcp_dir}")
-
- # 3. 检查MCP SDK
- sdk_path = os.path.join(mcp_dir, 'python-sdk', 'src')
- print(f"✓ SDK路径: {sdk_path}")
- print(f"✓ SDK存在: {os.path.exists(sdk_path)}")
-
- # 4. 检查必要文件
- server_script = os.path.join(mcp_dir, 'mcpserver_stdio.py')
- env_file = os.path.join(mcp_dir, '.env')
- print(f"✓ 服务器脚本: {os.path.exists(server_script)}")
- print(f"✓ 环境文件: {os.path.exists(env_file)}")
-
- # 5. 检查依赖包
- try:
- import mcp
- print("✓ MCP SDK: 已安装")
- except ImportError as e:
- print(f"❌ MCP SDK: 未安装 - {e}")
-
- try:
- import dashscope
- print("✓ DashScope: 已安装")
- except ImportError as e:
- print(f"❌ DashScope: 未安装 - {e}")
-
- try:
- import openai
- print("✓ OpenAI: 已安装")
- except ImportError as e:
- print(f"❌ OpenAI: 未安装 - {e}")
-
- # 6. 生成正确的配置
- python_exe = sys.executable.replace("\\", "\\\\")
- mcp_dir_escaped = mcp_dir.replace("\\", "\\\\")
- sdk_path_escaped = sdk_path.replace("\\", "\\\\")
-
- config = {
- "mcpServers": {
- "rimworld-knowledge-base": {
- "command": python_exe,
- "args": ["mcpserver_stdio.py"],
- "cwd": mcp_dir_escaped,
- "disabled": False,
- "alwaysAllow": [],
- "env": {
- "PYTHONPATH": sdk_path_escaped
- }
- }
- },
- "tools": {
- "rimworld-knowledge-base": {
- "description": "从RimWorld本地知识库(包括C#源码和XML)中检索上下文。",
- "server_name": "rimworld-knowledge-base",
- "tool_name": "get_context",
- "input_schema": {
- "type": "object",
- "properties": {
- "question": {
- "type": "string",
- "description": "关于RimWorld开发的问题,应包含代码或XML中的关键词。"
- }
- },
- "required": ["question"]
- }
- }
- }
- }
-
- print("\\n📋 建议的MCP配置:")
- print("=" * 50)
- print(json.dumps(config, indent=2, ensure_ascii=False))
-
- # 7. 测试服务器启动
- print("\\n🚀 测试服务器启动:")
- print("=" * 50)
- try:
- result = subprocess.run(
- [sys.executable, server_script],
- cwd=mcp_dir,
- capture_output=True,
- text=True,
- timeout=10
- )
- if result.returncode == 0:
- print("✓ 服务器可以正常启动")
- else:
- print(f"❌ 服务器启动失败: {result.stderr}")
- except subprocess.TimeoutExpired:
- print("✓ 服务器启动正常(超时保护触发)")
- except Exception as e:
- print(f"❌ 服务器测试失败: {e}")
-
- print("\\n🎯 配置建议:")
- print("=" * 50)
- print("1. 复制上面的配置到 Qoder IDE 的 MCP 设置中")
- print("2. 确保所有依赖包都已安装")
- print("3. 检查 .env 文件中的 API Key 配置")
- print("4. 重启 Qoder IDE 并重新连接 MCP 服务器")
-
-if __name__ == "__main__":
- test_mcp_configuration()
\ No newline at end of file
diff --git a/MCP/test_mcp.py b/MCP/test_mcp.py
deleted file mode 100644
index 4cea894a..00000000
--- a/MCP/test_mcp.py
+++ /dev/null
@@ -1,149 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-最终功能测试:验证MCP服务器是否能正常工作
-"""
-import os
-import sys
-import subprocess
-import time
-import json
-
-def test_mcp_server_final():
- """最终测试MCP服务器功能"""
- print("🔥 MCP服务器最终功能测试")
- print("=" * 50)
-
- # 获取当前目录
- mcp_dir = os.path.dirname(os.path.abspath(__file__))
- script_path = os.path.join(mcp_dir, 'mcpserver_stdio.py')
-
- try:
- # 1. 验证SDK安装
- try:
- import mcp
- print("✅ MCP SDK: 已正确安装")
- except ImportError:
- print("❌ MCP SDK: 未安装")
- return False
-
- # 2. 启动服务器
- print("🚀 启动MCP服务器...")
- process = subprocess.Popen(
- [sys.executable, script_path],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- text=True,
- cwd=mcp_dir
- )
-
- # 等待启动
- time.sleep(2)
-
- # 3. 初始化测试
- init_request = {
- "jsonrpc": "2.0",
- "id": 1,
- "method": "initialize",
- "params": {
- "protocolVersion": "2024-11-05",
- "capabilities": {},
- "clientInfo": {
- "name": "final-test-client",
- "version": "1.0.0"
- }
- }
- }
-
- print("📡 发送初始化请求...")
- process.stdin.write(json.dumps(init_request) + '\n')
- process.stdin.flush()
-
- # 读取初始化响应
- response = process.stdout.readline()
- if response:
- response_data = json.loads(response.strip())
- if "result" in response_data:
- print("✅ 初始化成功")
- print(f" 服务器名称: {response_data['result'].get('serverInfo', {}).get('name', 'unknown')}")
- print(f" 服务器版本: {response_data['result'].get('serverInfo', {}).get('version', 'unknown')}")
- else:
- print("❌ 初始化失败")
- return False
- else:
- print("❌ 初始化无响应")
- return False
-
- # 4. 工具列表测试
- tools_request = {
- "jsonrpc": "2.0",
- "id": 2,
- "method": "tools/list"
- }
-
- print("🔧 请求工具列表...")
- process.stdin.write(json.dumps(tools_request) + '\n')
- process.stdin.flush()
-
- tools_response = process.stdout.readline()
- if tools_response:
- tools_data = json.loads(tools_response.strip())
- if "result" in tools_data and "tools" in tools_data["result"]:
- tools = tools_data["result"]["tools"]
- print(f"✅ 发现 {len(tools)} 个工具:")
- for tool in tools:
- print(f" - {tool.get('name', 'unknown')}: {tool.get('description', 'no description')}")
- else:
- print("❌ 获取工具列表失败")
- else:
- print("❌ 工具列表请求无响应")
-
- print("\n🎯 测试结果:")
- print("✅ MCP服务器能够正常启动")
- print("✅ 初始化协议工作正常")
- print("✅ 工具发现机制正常")
- print("\n✨ 所有基本功能测试通过!")
-
- return True
-
- except Exception as e:
- print(f"❌ 测试过程中出错: {e}")
- return False
-
- finally:
- # 清理进程
- try:
- process.terminate()
- process.wait(timeout=5)
- except:
- try:
- process.kill()
- except:
- pass
-
-if __name__ == "__main__":
- print("开始最终测试...")
- success = test_mcp_server_final()
-
- if success:
- print("\n🎉 恭喜!MCP服务器已完全修复并正常工作!")
- print("\n📋 现在您需要在Qoder IDE中更新配置:")
- print("1. 打开Qoder IDE设置 → MCP")
- print("2. 更新配置文件,确保使用正确的绝对路径")
- print("3. 重启Qoder IDE")
- print("4. 在Agent模式下测试知识库查询")
- print("\n建议的配置:")
- print(json.dumps({
- "mcpServers": {
- "rimworld-knowledge-base": {
- "command": sys.executable,
- "args": ["mcpserver_stdio.py"],
- "cwd": os.path.dirname(os.path.abspath(__file__)),
- "disabled": False,
- "alwaysAllow": []
- }
- }
- }, indent=2))
- else:
- print("\n❌ 仍存在问题,需要进一步调试")
\ No newline at end of file
diff --git a/MCP/test_mcp_timeout_fix.py b/MCP/test_mcp_timeout_fix.py
deleted file mode 100644
index 2f0f803a..00000000
--- a/MCP/test_mcp_timeout_fix.py
+++ /dev/null
@@ -1,150 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-测试MCP服务器超时修复
-"""
-import os
-import sys
-import subprocess
-import time
-import json
-
-def test_mcp_server_timeout_fix():
- """测试MCP服务器是否能快速启动并响应"""
- print("开始测试MCP服务器超时修复...")
-
- # 获取当前目录
- mcp_dir = os.path.dirname(os.path.abspath(__file__))
- script_path = os.path.join(mcp_dir, 'mcpserver_stdio.py')
-
- try:
- # 启动MCP服务器进程
- print("启动MCP服务器...")
- start_time = time.time()
-
- process = subprocess.Popen(
- [sys.executable, script_path],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- text=True,
- cwd=mcp_dir
- )
-
- # 等待服务器启动(减少等待时间)
- time.sleep(2) # 从3秒减少到2秒
-
- startup_time = time.time() - start_time
- print(f"服务器启动耗时: {startup_time:.2f}秒")
-
- # 发送初始化请求
- init_request = {
- "jsonrpc": "2.0",
- "id": 1,
- "method": "initialize",
- "params": {
- "protocolVersion": "2024-11-05",
- "capabilities": {},
- "clientInfo": {
- "name": "test-client",
- "version": "1.0.0"
- }
- }
- }
-
- print("发送初始化请求...")
- request_start = time.time()
- process.stdin.write(json.dumps(init_request) + '\n')
- process.stdin.flush()
-
- # 读取响应
- response_line = process.stdout.readline()
- init_time = time.time() - request_start
-
- if response_line:
- print(f"✅ 初始化成功,耗时: {init_time:.2f}秒")
- print(f"收到响应: {response_line.strip()}")
- else:
- print("❌ 初始化失败:无响应")
- return False
-
- # 发送简单的工具调用请求
- tool_request = {
- "jsonrpc": "2.0",
- "id": 2,
- "method": "tools/call",
- "params": {
- "name": "get_context",
- "arguments": {
- "question": "ThingDef" # 简单的测试查询
- }
- }
- }
-
- print("发送工具调用请求...")
- tool_start = time.time()
- process.stdin.write(json.dumps(tool_request) + '\n')
- process.stdin.flush()
-
- # 等待响应(减少超时时间)
- timeout = 20 # 从30秒减少到20秒
- response_received = False
-
- while time.time() - tool_start < timeout:
- if process.poll() is not None:
- print("服务器进程已退出")
- break
-
- response_line = process.stdout.readline()
- if response_line:
- tool_time = time.time() - tool_start
- print(f"✅ 工具调用成功,耗时: {tool_time:.2f}秒")
- print(f"工具调用响应: {response_line.strip()[:200]}...") # 只显示前200个字符
- response_received = True
- break
-
- time.sleep(0.1)
-
- total_time = time.time() - start_time
-
- if response_received:
- print(f"✅ 测试成功:MCP服务器能够正常处理请求")
- print(f"总耗时: {total_time:.2f}秒")
-
- # 性能评估
- if total_time < 15:
- print("🚀 性能优秀:服务器响应速度很快")
- elif total_time < 25:
- print("✅ 性能良好:服务器响应速度可接受")
- else:
- print("⚠️ 性能一般:服务器响应较慢,可能仍有超时风险")
-
- else:
- print("❌ 测试失败:超时未收到响应")
- return False
-
- except Exception as e:
- print(f"❌ 测试出错: {e}")
- return False
-
- finally:
- # 清理进程
- try:
- process.terminate()
- process.wait(timeout=5)
- except:
- try:
- process.kill()
- except:
- pass
- print("测试完成")
-
- return True
-
-if __name__ == "__main__":
- success = test_mcp_server_timeout_fix()
- if success:
- print("\n🎉 MCP服务器超时问题已修复!")
- print("现在可以在Qoder IDE中重新连接MCP服务器了。")
- else:
- print("\n❌ MCP服务器仍存在问题,需要进一步调试。")
\ No newline at end of file
diff --git a/MCP/web_api_server.py b/MCP/web_api_server.py
deleted file mode 100644
index 5a29b922..00000000
--- a/MCP/web_api_server.py
+++ /dev/null
@@ -1,249 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-RimWorld知识库Web API服务器
-提供HTTP接口,可以通过浏览器或HTTP客户端访问
-"""
-import os
-import sys
-import json
-from http.server import HTTPServer, BaseHTTPRequestHandler
-from urllib.parse import urlparse, parse_qs
-import threading
-import webbrowser
-
-# 添加MCP路径
-MCP_DIR = os.path.dirname(os.path.abspath(__file__))
-SDK_PATH = os.path.join(MCP_DIR, 'python-sdk', 'src')
-if SDK_PATH not in sys.path:
- sys.path.insert(0, SDK_PATH)
-
-class RimWorldAPIHandler(BaseHTTPRequestHandler):
- """HTTP请求处理器"""
-
- def do_GET(self):
- """处理GET请求"""
- parsed_url = urlparse(self.path)
-
- if parsed_url.path == '/':
- self.serve_web_interface()
- elif parsed_url.path == '/query':
- self.handle_query_get(parsed_url)
- elif parsed_url.path == '/api/query':
- self.handle_api_query_get(parsed_url)
- else:
- self.send_error(404, "Not Found")
-
- def do_POST(self):
- """处理POST请求"""
- if self.path == '/api/query':
- self.handle_api_query_post()
- else:
- self.send_error(404, "Not Found")
-
- def serve_web_interface(self):
- """提供Web界面"""
- html = """
-
-
-
-
-
- RimWorld 知识库
-
-
-
-
-
-
-
-
-
-
-
-
💡 查询示例:
-
• ThingDef的定义和用法
-
• 如何创建Building
-
• Pawn类的主要方法
-
• CompPower的使用
-
-
-
-
-
-
-
- """
-
- self.send_response(200)
- self.send_header('Content-Type', 'text/html; charset=utf-8')
- self.end_headers()
- self.wfile.write(html.encode('utf-8'))
-
- def handle_query_get(self, parsed_url):
- """处理GET查询请求"""
- params = parse_qs(parsed_url.query)
- question = params.get('q', [''])[0]
-
- if not question:
- self.send_error(400, "Missing 'q' parameter")
- return
-
- try:
- from mcpserver_stdio import get_context
- result = get_context(question)
-
- self.send_response(200)
- self.send_header('Content-Type', 'text/plain; charset=utf-8')
- self.end_headers()
- self.wfile.write(result.encode('utf-8'))
- except Exception as e:
- self.send_error(500, f"Query failed: {e}")
-
- def handle_api_query_get(self, parsed_url):
- """处理API GET查询"""
- params = parse_qs(parsed_url.query)
- question = params.get('q', [''])[0]
-
- if not question:
- response = {"success": False, "error": "Missing 'q' parameter"}
- else:
- try:
- from mcpserver_stdio import get_context
- result = get_context(question)
- response = {"success": True, "result": result}
- except Exception as e:
- response = {"success": False, "error": str(e)}
-
- self.send_response(200)
- self.send_header('Content-Type', 'application/json; charset=utf-8')
- self.end_headers()
- self.wfile.write(json.dumps(response, ensure_ascii=False).encode('utf-8'))
-
- def handle_api_query_post(self):
- """处理API POST查询"""
- content_length = int(self.headers['Content-Length'])
- post_data = self.rfile.read(content_length)
-
- try:
- data = json.loads(post_data.decode('utf-8'))
- question = data.get('question', '')
-
- if not question:
- response = {"success": False, "error": "Missing 'question' field"}
- else:
- from mcpserver_stdio import get_context
- result = get_context(question)
- response = {"success": True, "result": result}
- except Exception as e:
- response = {"success": False, "error": str(e)}
-
- self.send_response(200)
- self.send_header('Content-Type', 'application/json; charset=utf-8')
- self.send_header('Access-Control-Allow-Origin', '*')
- self.end_headers()
- self.wfile.write(json.dumps(response, ensure_ascii=False).encode('utf-8'))
-
- def log_message(self, format, *args):
- """自定义日志输出"""
- print(f"[{self.address_string()}] {format % args}")
-
-def start_server(port=8080, open_browser=True):
- """启动Web服务器"""
- server_address = ('', port)
- httpd = HTTPServer(server_address, RimWorldAPIHandler)
-
- print(f"🌐 RimWorld知识库Web服务器启动")
- print(f"📍 服务地址: http://localhost:{port}")
- print(f"🔍 查询API: http://localhost:{port}/api/query?q=您的问题")
- print(f"💻 Web界面: http://localhost:{port}")
- print("按 Ctrl+C 停止服务器")
-
- if open_browser:
- # 延迟打开浏览器
- def open_browser_delayed():
- import time
- time.sleep(1)
- webbrowser.open(f'http://localhost:{port}')
-
- thread = threading.Thread(target=open_browser_delayed)
- thread.daemon = True
- thread.start()
-
- try:
- httpd.serve_forever()
- except KeyboardInterrupt:
- print("\n🛑 服务器已停止")
- httpd.shutdown()
-
-if __name__ == "__main__":
- import argparse
-
- parser = argparse.ArgumentParser(description='RimWorld知识库Web API服务器')
- parser.add_argument('--port', '-p', type=int, default=8080, help='服务器端口 (默认: 8080)')
- parser.add_argument('--no-browser', action='store_true', help='不自动打开浏览器')
-
- args = parser.parse_args()
-
- start_server(args.port, not args.no_browser)
\ No newline at end of file
diff --git a/MCP/使用指南.md b/MCP/使用指南.md
deleted file mode 100644
index 9fd0dcb5..00000000
--- a/MCP/使用指南.md
+++ /dev/null
@@ -1,102 +0,0 @@
-# RimWorld 知识库 - 绕过 Qoder IDE 使用指南
-
-由于 Qoder IDE 中的 MCP 连接可能存在问题,我们提供了多种直接访问 RimWorld 知识库的方法。
-
-## 🚀 方法 1:直接 Python 调用
-
-最简单直接的方法:
-
-```bash
-# 直接查询
-python direct_mcp_client.py -q "ThingDef是什么"
-
-# 交互模式
-python direct_mcp_client.py -i
-
-# 查看帮助
-python direct_mcp_client.py -h
-```
-
-### 优点:
-- ✅ 最快速,无需额外依赖
-- ✅ 支持交互模式
-- ✅ 直接在命令行使用
-
-## 🛠️ 方法 2:命令行工具
-
-专业的命令行查询工具:
-
-```bash
-# 基本查询
-python rimworld_query.py "ThingDef的定义"
-
-# 保存结果到文件
-python rimworld_query.py "Building类的方法" --output building_info.txt
-
-# 显示原始结果(不格式化)
-python rimworld_query.py "Pawn类" --raw
-
-# 查看示例
-python rimworld_query.py --list-examples
-```
-
-### 优点:
-- ✅ 结果可保存到文件
-- ✅ 支持原始输出格式
-- ✅ 内置查询示例
-
-## 📝 常用查询示例
-
-```bash
-# 查询类定义
-"ThingDef的定义和用法"
-"Building类有哪些方法"
-"Pawn类的构造函数"
-
-# 查询特定方法
-"GenConstruct.CanPlaceBlueprintAt 方法"
-"Building_Door 的开关逻辑"
-"CompPower 的电力管理"
-
-# 查询XML相关
-"XML中的defName规则"
-"如何定义新的ThingDef"
-"建筑物的XML结构"
-```
-
-## 🔧 故障排除
-
-### 如果出现导入错误:
-```bash
-# 确保在正确的目录
-cd "C:\Steam\steamapps\common\RimWorld\Mods\3516260226\MCP"
-
-# 检查 Python 环境
-python -c "import mcp; print('MCP SDK 正常')"
-```
-
-### 如果查询结果为空:
-- 尝试使用更具体的关键词
-- 检查关键词拼写
-- 使用英文类名或方法名
-
-### 如果 Web 服务器无法启动:
-- 检查端口是否被占用
-- 尝试使用不同的端口号
-- 确保没有其他程序占用该端口
-
-## 💡 推荐使用场景
-
-- **快速查询**: 使用方法 1 (direct_mcp_client.py)
-- **批量处理**: 使用方法 2 (rimworld_query.py)
-- **团队共享**: 使用方法 3 (web_api_server.py)
-- **集成开发**: 使用 Web API 接口
-
-## 🎯 性能优化
-
-所有方法都已经过优化:
-- 向量化处理限制在 10 个文件以内
-- API 调用超时设置为 12-15 秒
-- 支持本地缓存加速重复查询
-
-现在您可以完全绕过 Qoder IDE,直接使用 RimWorld 知识库了!
\ No newline at end of file
diff --git a/MCP/使用转发服务指南.md b/MCP/使用转发服务指南.md
deleted file mode 100644
index 8fba0697..00000000
--- a/MCP/使用转发服务指南.md
+++ /dev/null
@@ -1,207 +0,0 @@
-# OpenAI兼容接口到阿里云百炼平台智能体应用转发服务使用指南
-
-本文档介绍了如何使用Python编写的转发服务,将OpenAI兼容的API请求转换为阿里云百炼平台智能体应用的请求。
-
-## 工作原理
-
-该转发服务作为一个代理服务器运行,接收OpenAI兼容的API请求,并将其转换为阿里云百炼平台智能体应用的请求格式,然后将响应转换回OpenAI格式返回给客户端。
-
-```
-客户端 -> OpenAI兼容请求 -> 转发服务 -> 阿里云百炼平台 -> 转发服务 -> OpenAI兼容响应 -> 客户端
-```
-
-## 配置要求
-
-### 环境变量设置
-
-需要设置以下环境变量:
-
-1. `DASHSCOPE_API_KEY` - 阿里云百炼平台的API Key
-2. `DASHSCOPE_APP_ID` - 智能体应用的APP ID
-3. `PROXY_PORT` (可选) - 转发服务监听的端口,默认为8000
-
-您可以在项目目录下的 [.env](file://c:\Steam\steamapps\common\RimWorld\Mods\3516260226\MCP\.env) 文件中配置这些变量:
-
-```env
-DASHSCOPE_API_KEY="sk-xxxxxxxx"
-DASHSCOPE_APP_ID="app-xxxxxxxx"
-PROXY_PORT=8000
-```
-
-在Windows系统中设置环境变量的示例:
-```cmd
-set DASHSCOPE_API_KEY=your_api_key_here
-set DASHSCOPE_APP_ID=your_app_id_here
-set PROXY_PORT=8000
-```
-
-在Linux/macOS系统中设置环境变量的示例:
-```bash
-export DASHSCOPE_API_KEY=your_api_key_here
-export DASHSCOPE_APP_ID=your_app_id_here
-export PROXY_PORT=8000
-```
-
-## 启动服务
-
-运行转发服务:
-
-```bash
-python openai_to_dashscope_proxy.py
-```
-
-服务启动后,将显示以下信息:
-```
-OpenAI到阿里云百炼平台转发服务启动,监听端口 8000
-Base URL: http://localhost:8000/v1
-模型名称: dashscope-app
-```
-
-## 在Kilo Code中配置
-
-在Kilo Code中按以下步骤配置:
-
-1. 打开Kilo Code设置面板
-2. 选择"API Provider"为"OpenAI Compatible"
-3. 设置Base URL为: `http://localhost:8000/v1` (如果使用默认端口)
-4. API Key可以任意填写(服务不会验证)
-5. 模型名称填写: `dashscope-app`
-
-## 支持的功能
-
-### 1. 基本文本对话
-
-发送聊天完成请求:
-```json
-{
- "model": "dashscope-app",
- "messages": [
- {
- "role": "user",
- "content": "你好,你是谁?"
- }
- ]
-}
-```
-
-### 2. 流式输出
-
-启用流式输出:
-```json
-{
- "model": "dashscope-app",
- "messages": [
- {
- "role": "user",
- "content": "讲一个有趣的故事"
- }
- ],
- "stream": true
-}
-```
-
-## 技术细节
-
-### 请求转换
-
-OpenAI兼容请求格式:
-```json
-{
- "model": "dashscope-app",
- "messages": [
- {"role": "user", "content": "提示词"}
- ]
-}
-```
-
-转换为阿里云百炼平台请求格式:
-```json
-{
- "input": {
- "prompt": "提示词"
- },
- "parameters": {},
- "debug": {}
-}
-```
-
-### 响应转换
-
-阿里云百炼平台响应格式:
-```json
-{
- "output": {
- "text": "响应内容",
- "finish_reason": "stop"
- }
-}
-```
-
-转换为OpenAI兼容响应格式:
-```json
-{
- "id": "chatcmpl-request_id",
- "object": "chat.completion",
- "created": 1234567890,
- "model": "dashscope-app",
- "choices": [
- {
- "index": 0,
- "message": {
- "role": "assistant",
- "content": "响应内容"
- },
- "finish_reason": "stop"
- }
- ]
-}
-```
-
-## 故障排除
-
-### 1. 环境变量未设置
-
-错误信息:
-```
-ValueError: 请设置环境变量 DASHSCOPE_API_KEY
-```
-
-解决方案:
-确保已正确设置环境变量 `DASHSCOPE_API_KEY` 和 `DASHSCOPE_APP_ID`。
-
-### 2. 网络连接问题
-
-错误信息:
-```
-requests.exceptions.ConnectionError
-```
-
-解决方案:
-检查网络连接,确保可以访问阿里云百炼平台。
-
-### 3. API Key或APP ID错误
-
-错误信息:
-```
-401 Unauthorized
-```
-
-解决方案:
-检查API Key和APP ID是否正确。
-
-### 4. 端口被占用
-
-错误信息:
-```
-OSError: [Errno 98] Address already in use
-```
-
-解决方案:
-更改端口号,通过设置 `PROXY_PORT` 环境变量或修改代码中的默认端口。
-
-## 最佳实践
-
-1. **安全性**:在生产环境中,建议将转发服务部署在安全的网络环境中
-2. **监控**:启用日志记录以便监控服务运行状态
-3. **错误处理**:确保正确处理各种错误情况
-4. **性能**:对于高并发场景,考虑使用异步框架如FastAPI替代内置的HTTP服务器
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/3516260226.code-workspace b/Source/WulaFallenEmpire/3516260226.code-workspace
index 9604a404..a2433624 100644
--- a/Source/WulaFallenEmpire/3516260226.code-workspace
+++ b/Source/WulaFallenEmpire/3516260226.code-workspace
@@ -13,20 +13,7 @@
"path": "../../../../Data"
},
{
- "name": "AlienRace",
- "path": "../../../../../../../../Users/Kalo/Downloads/AlienRaces/Source/AlienRace/AlienRace"
- },
- {
- "path": "../../../../../../workshop/content/294100/3565275325/Source/SRALib/SRALib"
- },
- {
- "path": "../../../../../../workshop/content/294100/3575567766"
- },
- {
- "path": "../../../../../../../../Users/Kalo/Downloads/MechsuitFramework-main"
- },
- {
- "path": "../../../../../../workshop/content/294100/3226701491/1.6/Assemblies/BM_PowerArmor"
+ "path": "../../../../dll1.6"
}
],
"settings": {}
diff --git a/mod.vdf b/mod.vdf
deleted file mode 100644
index a6b7699b..00000000
--- a/mod.vdf
+++ /dev/null
@@ -1,11 +0,0 @@
-
-"workshopitem"
-{
- "appid" "294100"
- "contentfolder" "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\WulaFallenEmpireTest"
- "previewfile" "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\WulaFallenEmpireTest\\About\\Preview.png"
- "visibility" "3"
- "title" "WulaFallenEmpire V2"
- "changenote" "1.6"
- "publishedfileid" "3604325124"
-}
\ No newline at end of file