mcp
This commit is contained in:
38
mcp/data/id_rsa.txt
Normal file
38
mcp/data/id_rsa.txt
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
-----END OPENSSH PRIVATE KEY-----
|
||||||
56
mcp/server.py
Normal file
56
mcp/server.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# server.py
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import asyncssh # 用于 SSH 连接的库
|
||||||
|
from mcp.server.fastmcp import FastMCP, Context
|
||||||
|
|
||||||
|
# --- 预设的服务器连接信息 ---
|
||||||
|
# 警告:在生产环境中,不要将密钥和密码硬编码在代码里。
|
||||||
|
# 最好使用环境变量、配置文件或专门的密钥管理服务。
|
||||||
|
REMOTE_HOST = '202.121.182.208' # 替换为你的服务器地址
|
||||||
|
REMOTE_USER = 'koko125' # 替换为你的用户名
|
||||||
|
PRIVATE_KEY_PATH = 'data/id_rsa.txt' # 替换为你的私钥文件路径
|
||||||
|
|
||||||
|
# 创建一个 MCP 服务器实例
|
||||||
|
mcp = FastMCP("远程服务器工具")
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
async def execute_remote_command(command: str, ctx: Context) -> str:
|
||||||
|
"""
|
||||||
|
在远程服务器上执行一个shell命令并返回其输出。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
command: 要在远程服务器上执行的命令字符串。
|
||||||
|
"""
|
||||||
|
await ctx.info(f"准备在 {REMOTE_HOST} 上执行命令: '{command}'")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 建立 SSH 连接
|
||||||
|
async with asyncssh.connect(REMOTE_HOST, username=REMOTE_USER, client_keys=[PRIVATE_KEY_PATH]) as conn:
|
||||||
|
# 执行命令
|
||||||
|
result = await conn.run(command, check=True)
|
||||||
|
|
||||||
|
# 成功执行,返回标准输出
|
||||||
|
output = result.stdout.strip()
|
||||||
|
await ctx.info(f"命令成功执行,返回输出。")
|
||||||
|
await ctx.debug(f"输出内容: {output}")
|
||||||
|
return output
|
||||||
|
|
||||||
|
except asyncssh.ProcessError as e:
|
||||||
|
# 命令执行出错(例如,命令本身返回了非零退出码)
|
||||||
|
error_message = f"命令执行失败: {e.stderr.strip()}"
|
||||||
|
await ctx.error(error_message)
|
||||||
|
return error_message
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# 其他连接错误等
|
||||||
|
error_message = f"发生未知错误: {str(e)}"
|
||||||
|
await ctx.error(error_message)
|
||||||
|
return error_message
|
||||||
|
|
||||||
|
|
||||||
|
# 这部分使得你可以直接运行 `python server.py`
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# mcp.run() # 这是默认的 stdio 模式
|
||||||
|
mcp.run(transport="streamable-http") # 改为 streamable-http 模式
|
||||||
19
softBV/out/Br.csv
Normal file
19
softBV/out/Br.csv
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
filename,formula,conductivity(e-3S/m)
|
||||||
|
1080005.cif,Cs3Li2Br5,21.23
|
||||||
|
1147619.cif,Li3YBr6,20.00
|
||||||
|
1147621.cif,Li3InBr6,7.23
|
||||||
|
1211043.cif,LiFeBr4,11.60
|
||||||
|
1222492.cif,Li3ErBr6,29.61
|
||||||
|
1222679.cif,Li2MnBr4,9.68
|
||||||
|
2033990.cif,CsLi2Br3,9.71
|
||||||
|
22967.cif,Li2FeBr4,11.21
|
||||||
|
23057.cif,CsLiBr2,5.12
|
||||||
|
2763849.cif,Cs3Li2Br5,121.34
|
||||||
|
28237.cif,RbLiBr2,3.46
|
||||||
|
28250.cif,Li2MnBr4,12.25
|
||||||
|
28326.cif,LiGaBr4,1.56
|
||||||
|
28327.cif,LiGaBr3,8.32
|
||||||
|
28829.cif,Li2ZnBr4,15.71
|
||||||
|
37873.cif,Li3ErBr6,10.90
|
||||||
|
580554.cif,CsLi3Br4,38.40
|
||||||
|
606680.cif,CsLi2Br3,20.63
|
||||||
|
@@ -3,17 +3,16 @@ import sys
|
|||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
import csv
|
import csv
|
||||||
|
import argparse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# --- 配置区 ---
|
# --- 配置区 ---
|
||||||
# softBV.x 可执行文件的路径,请根据你的实际情况修改
|
# softBV.x 可执行文件的路径,请根据你的实际情况修改
|
||||||
# 使用 Path.home() 来自动获取用户家目录,比 '~' 更可靠
|
|
||||||
SOFTBV_EXECUTABLE = Path.home() / "tool/softBV-GUI_linux/bin/softBV.x"
|
SOFTBV_EXECUTABLE = Path.home() / "tool/softBV-GUI_linux/bin/softBV.x"
|
||||||
|
|
||||||
# 固定的命令参数
|
# 固定的命令参数
|
||||||
MOBILE_ION = "Li"
|
MOBILE_ION = "Li"
|
||||||
ION_VALENCE = "1"
|
ION_VALENCE = "1"
|
||||||
SF_VALUE = "1000"
|
|
||||||
|
|
||||||
# 输出CSV文件的名称
|
# 输出CSV文件的名称
|
||||||
OUTPUT_CSV_FILE = "conductivity_results.csv"
|
OUTPUT_CSV_FILE = "conductivity_results.csv"
|
||||||
@@ -33,47 +32,73 @@ def check_executable():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def get_formula_from_cif_line2(cif_path):
|
||||||
|
"""
|
||||||
|
通过读取CIF文件的第二行来提取化学式。
|
||||||
|
期望的格式是 'data_FORMULA'。
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(cif_path, 'r', encoding='utf-8') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
if len(lines) > 1:
|
||||||
|
second_line = lines[1].strip() # .strip() 去除首尾空白和换行符
|
||||||
|
if second_line.lower().startswith('data_'):
|
||||||
|
# 提取 'data_' 之后的内容作为化学式
|
||||||
|
return second_line[5:]
|
||||||
|
else:
|
||||||
|
print(f"\n警告: 文件 {cif_path.name} 的第二行格式不正确 (不是以'data_'开头)。", file=sys.stderr)
|
||||||
|
return "FormatError"
|
||||||
|
else:
|
||||||
|
print(f"\n警告: 文件 {cif_path.name} 行数不足2行。", file=sys.stderr)
|
||||||
|
return "FileTooShort"
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n警告: 读取文件 {cif_path.name} 时发生错误: {e}", file=sys.stderr)
|
||||||
|
return "ReadError"
|
||||||
|
|
||||||
|
|
||||||
def parse_conductivity(output_text):
|
def parse_conductivity(output_text):
|
||||||
"""
|
"""从 softBV.x 的输出文本中解析并格式化电导率"""
|
||||||
从 softBV.x 的输出文本中解析电导率(conductivity)。
|
|
||||||
使用正则表达式来匹配 'MD: conductivity = <value>' 格式的行。
|
|
||||||
"""
|
|
||||||
# 正则表达式解释:
|
|
||||||
# ^ - 匹配行首
|
|
||||||
# \s* - 匹配任意数量的空白字符
|
|
||||||
# MD: conductivity - 匹配字面字符串
|
|
||||||
# \s*=\s* - 匹配等号,前后可以有任意空白
|
|
||||||
# ([-\d.eE+]+) - 捕获组1: 匹配并捕获一个或多个数字、点、'e'/'E'、'+'/'-' (科学计数法)
|
|
||||||
pattern = re.compile(r"^\s*MD: conductivity\s*=\s*([-\d.eE+]+)", re.MULTILINE)
|
pattern = re.compile(r"^\s*MD: conductivity\s*=\s*([-\d.eE+]+)", re.MULTILINE)
|
||||||
match = pattern.search(output_text)
|
match = pattern.search(output_text)
|
||||||
|
|
||||||
if match:
|
if match:
|
||||||
# 返回捕获到的第一个组 (也就是数值部分)
|
conductivity_str = match.group(1)
|
||||||
return match.group(1)
|
try:
|
||||||
|
conductivity_val = float(conductivity_str)
|
||||||
|
# 将单位从 S/m 转换为 1e-3 S/m (mS/m), 需要乘以 1000
|
||||||
|
conductivity_ms_m = conductivity_val * 1000
|
||||||
|
# 格式化为两位小数的字符串
|
||||||
|
return f"{conductivity_ms_m:.2f}"
|
||||||
|
except ValueError:
|
||||||
|
return "ValueError"
|
||||||
else:
|
else:
|
||||||
# 如果没有找到匹配的行,返回 None
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""主函数"""
|
"""主函数"""
|
||||||
# 1. 检查命令行参数
|
parser = argparse.ArgumentParser(
|
||||||
if len(sys.argv) != 2:
|
description="运行 softBV.x 计算电导率并汇总到CSV文件。",
|
||||||
print(f"用法: python {sys.argv[0]} <cif_folder_name>")
|
formatter_class=argparse.RawTextHelpFormatter
|
||||||
print(f"例如: python {sys.argv[0]} Br")
|
)
|
||||||
sys.exit(1)
|
parser.add_argument("folder", type=str, help="包含CIF文件的目标文件夹路径。")
|
||||||
|
parser.add_argument(
|
||||||
|
"-t", "--temperature",
|
||||||
|
type=int,
|
||||||
|
default=1000,
|
||||||
|
help="模拟温度 (K),对应 softBV.x 的最后一个参数。\n默认值: 1000"
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
target_folder = Path(sys.argv[1])
|
target_folder = Path(args.folder)
|
||||||
|
temperature = str(args.temperature)
|
||||||
|
|
||||||
# 检查目标文件夹是否存在
|
|
||||||
if not target_folder.is_dir():
|
if not target_folder.is_dir():
|
||||||
print(f"错误: 文件夹 '{target_folder}' 不存在。")
|
print(f"错误: 文件夹 '{target_folder}' 不存在。")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# 检查 softBV.x 是否配置正确
|
|
||||||
check_executable()
|
check_executable()
|
||||||
|
|
||||||
# 2. 查找所有 .cif 文件
|
|
||||||
cif_files = sorted(list(target_folder.glob("*.cif")))
|
cif_files = sorted(list(target_folder.glob("*.cif")))
|
||||||
|
|
||||||
if not cif_files:
|
if not cif_files:
|
||||||
@@ -81,61 +106,45 @@ def main():
|
|||||||
return
|
return
|
||||||
|
|
||||||
print(f"在 '{target_folder}' 中找到 {len(cif_files)} 个 .cif 文件,开始处理...")
|
print(f"在 '{target_folder}' 中找到 {len(cif_files)} 个 .cif 文件,开始处理...")
|
||||||
|
print(f"模拟温度设置为: {temperature} K")
|
||||||
|
|
||||||
results = [] # 用于存储 (文件名, 电导率)
|
results = []
|
||||||
|
|
||||||
# 3. 遍历文件,执行命令并提取数据
|
|
||||||
for cif_file in cif_files:
|
for cif_file in cif_files:
|
||||||
print(f" -> 正在处理: {cif_file} ...", end="", flush=True)
|
print(f" -> 正在处理: {cif_file} ...", end="", flush=True)
|
||||||
|
|
||||||
# 构建命令
|
# 使用新的函数获取化学式
|
||||||
|
formula = get_formula_from_cif_line2(cif_file)
|
||||||
|
|
||||||
command = [
|
command = [
|
||||||
str(SOFTBV_EXECUTABLE),
|
str(SOFTBV_EXECUTABLE), "--md", str(cif_file),
|
||||||
"--md",
|
MOBILE_ION, ION_VALENCE, temperature
|
||||||
str(cif_file),
|
|
||||||
MOBILE_ION,
|
|
||||||
ION_VALENCE,
|
|
||||||
SF_VALUE
|
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 执行命令并捕获输出
|
|
||||||
# capture_output=True 将 stdout 和 stderr 分别捕获
|
|
||||||
# text=True (或 encoding='utf-8') 将输出解码为字符串
|
|
||||||
# check=True 如果命令返回非零退出码 (表示错误),则会抛出异常
|
|
||||||
process_result = subprocess.run(
|
process_result = subprocess.run(
|
||||||
command,
|
command, capture_output=True, text=True, check=True, timeout=300
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
check=True,
|
|
||||||
timeout=300 # 设置一个超时时间(例如300秒),防止程序卡死
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 从标准输出中解析数据
|
|
||||||
conductivity = parse_conductivity(process_result.stdout)
|
conductivity = parse_conductivity(process_result.stdout)
|
||||||
|
|
||||||
if conductivity is not None:
|
if conductivity is not None:
|
||||||
results.append((cif_file.name, conductivity))
|
results.append([cif_file.name, formula, conductivity])
|
||||||
print(f" 成功, conductivity = {conductivity}")
|
print(f" 成功, Formula: {formula}, Conductivity: {conductivity} (x10^-3 S/m)")
|
||||||
else:
|
else:
|
||||||
print(" 失败 (无法在输出中找到conductivity)")
|
print(" 失败 (无法在输出中找到conductivity)")
|
||||||
# 你也可以选择将错误信息记录下来
|
results.append([cif_file.name, formula, "NotFound"])
|
||||||
# results.append((cif_file.name, "Error: Not Found"))
|
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
# 命令执行失败
|
|
||||||
print(f" 失败 (命令执行错误)")
|
print(f" 失败 (命令执行错误)")
|
||||||
print(f" 错误信息: {e.stderr.strip()}")
|
print(f" 错误信息: {e.stderr.strip()}", file=sys.stderr)
|
||||||
# results.append((cif_file.name, "Error: Execution Failed"))
|
results.append([cif_file.name, formula, "ExecError"])
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
print(f" 失败 (命令执行超时)")
|
print(f" 失败 (命令执行超时)")
|
||||||
# results.append((cif_file.name, "Error: Timeout"))
|
results.append([cif_file.name, formula, "Timeout"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# 其他未知错误
|
print(f" 失败 (发生未知错误: {e})", file=sys.stderr)
|
||||||
print(f" 失败 (发生未知错误: {e})")
|
results.append([cif_file.name, formula, "UnknownError"])
|
||||||
# results.append((cif_file.name, f"Error: {e}"))
|
|
||||||
|
|
||||||
# 4. 将结果写入CSV文件
|
|
||||||
if not results:
|
if not results:
|
||||||
print("没有成功处理任何文件,不生成CSV。")
|
print("没有成功处理任何文件,不生成CSV。")
|
||||||
return
|
return
|
||||||
@@ -145,9 +154,7 @@ def main():
|
|||||||
try:
|
try:
|
||||||
with open(OUTPUT_CSV_FILE, 'w', newline='', encoding='utf-8') as csvfile:
|
with open(OUTPUT_CSV_FILE, 'w', newline='', encoding='utf-8') as csvfile:
|
||||||
writer = csv.writer(csvfile)
|
writer = csv.writer(csvfile)
|
||||||
# 写入表头
|
writer.writerow(['filename', 'formula', 'conductivity(e-3S/m)'])
|
||||||
writer.writerow(['filename', 'conductivity'])
|
|
||||||
# 写入数据
|
|
||||||
writer.writerows(results)
|
writer.writerows(results)
|
||||||
print("CSV文件已成功生成!")
|
print("CSV文件已成功生成!")
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user