feat(mcp): 增加游戏内方块调试桥接

新增 stdio MCP server 和 mcp 子命令,通过调试会话状态文件连接内置 TCP bridge,并提供 place_block 工具。

Bridge 将请求投递到服务端 System 的游戏线程执行,限制请求大小与队列长度,并按会话端口安全清理状态文件和连接。
This commit is contained in:
2026-07-21 21:04:45 +08:00
parent 94b1b9a1f5
commit 1a7bc6aca1
15 changed files with 1375 additions and 11 deletions

View File

@@ -40,6 +40,7 @@ pub fn launch_game(
config: &DebugConfig,
config_arg: Option<&Path>,
mod_dirs: &[ResolvedModDir],
mcp_state_path: Option<&Path>,
) -> Result<()> {
let enable_ipc = config.auto_hot_reload_mods;
let ipc = if enable_ipc {
@@ -52,9 +53,10 @@ pub fn launch_game(
let command = build_command(config, config_arg);
let mut command_w = wide_null(OsStr::new(&command));
let env_block = if ipc.is_some() || config.auto_hot_reload_ui {
let env_block = if ipc.is_some() || mcp_state_path.is_some() || config.auto_hot_reload_ui {
Some(build_environment_block(
ipc.as_ref().map(|server| server.port()),
mcp_state_path,
))
} else {
None
@@ -154,6 +156,7 @@ pub fn launch_game(
_config: &DebugConfig,
_config_arg: Option<&Path>,
_mod_dirs: &[ResolvedModDir],
_mcp_state_path: Option<&Path>,
) -> Result<()> {
Err(CliError::InvalidInput(
"debug launch is only supported on Windows".to_string(),
@@ -230,11 +233,12 @@ where
}
#[cfg(windows)]
fn build_environment_block(ipc_port: Option<u16>) -> Vec<u16> {
fn build_environment_block(ipc_port: Option<u16>, mcp_state_path: Option<&Path>) -> Vec<u16> {
let mut pairs: Vec<(Vec<u16>, Vec<u16>)> = std::env::vars_os()
.filter(|(key, _)| {
let key = key.to_string_lossy();
!key.eq_ignore_ascii_case("MCDEV_DEBUG_IPC_PORT")
&& !key.eq_ignore_ascii_case("MCDEV_MCP_BRIDGE_STATE")
})
.map(|(key, value)| (key.encode_wide().collect(), value.encode_wide().collect()))
.collect();
@@ -245,6 +249,13 @@ fn build_environment_block(ipc_port: Option<u16>) -> Vec<u16> {
OsStr::new(&port.to_string()).encode_wide().collect(),
));
}
if let Some(path) = mcp_state_path {
let state_path = path.to_string_lossy().replace('\\', "/");
pairs.push((
OsStr::new("MCDEV_MCP_BRIDGE_STATE").encode_wide().collect(),
OsStr::new(&state_path).encode_wide().collect(),
));
}
pairs.sort_by(|a, b| a.0.cmp(&b.0));
let mut block = Vec::new();