提交qiming-mcp-proxy

This commit is contained in:
Codex
2026-06-01 13:03:20 +08:00
parent 9e9486b7c2
commit afb3d9f4e6
394 changed files with 124494 additions and 0 deletions

View File

@@ -0,0 +1,511 @@
# mcp-proxy 启动失败分析
## 问题描述
从 nuwax-agent 日志中发现mcp-proxy 启动后健康检查持续失败:
```
2026-02-12T06:19:01.052725Z ERROR [McpProxy] 健康检查失败:
MCP Proxy 健康检查超时: 等待 15s 后 http://127.0.0.1:18099/mcp 仍未就绪
```
**启动日志**
```
2026-02-12T06:18:41.088275Z INFO [McpProxy] 可执行文件路径: C:\Users\MECHREVO\.local\bin\mcp-proxy.cmd
2026-02-12T06:18:41.088293Z INFO [McpProxy] 监听地址: 127.0.0.1:18099
2026-02-12T06:18:41.088337Z INFO [McpProxy] Windows: 添加 npm 全局 bin 到 PATH: C:\Users\MECHREVO\AppData\Roaming\npm
... (15 秒后)
2026-02-12T06:19:01.052725Z ERROR [McpProxy] 健康检查失败
```
---
## 关键问题
### 1. 缺少子进程输出
由于 CMD 窗口可能被隐藏或者 nuwax-agent 没有捕获 stdout/stderr**看不到 mcp-proxy 的实际错误信息**。
这就像在黑暗中调试:
```
nuwax-agent: "mcp-proxy你启动了吗"
[15 秒沉默]
nuwax-agent: "超时了,你失败了!"
mcp-proxy: (可能早就崩溃了,但没人知道为什么)
```
### 2. 健康检查端点可能不对
日志显示健康检查 URL
```
http://127.0.0.1:18099/mcp
```
**需要验证**mcp-proxy 的健康检查端点是否真的是 `/mcp`
根据 mcp-proxy 代码,让我检查实际的路由:
---
## 诊断步骤
### 步骤 1: 检查 mcp-proxy 实际的健康检查路由
从 mcp-proxy 的代码分析(假设基于之前的代码结构):
**可能的健康检查端点**
- `/health` - 标准健康检查
- `/` - 根路径
- `/mcp` - MCP 服务列表
- `/api/health` - API 健康检查
**需要确认**: nuwax-agent 使用的 `/mcp` 端点是否正确。
### 步骤 2: 手动测试 mcp-proxy
在 Windows 测试机上手动运行:
```powershell
# 1. 手动启动 mcp-proxy
cd C:\Users\MECHREVO\.local\bin
.\mcp-proxy.cmd --help
# 2. 查看可用命令
.\mcp-proxy.cmd server --help
# 3. 手动启动服务器(查看完整输出)
.\mcp-proxy.cmd server --port 18099
# 4. 在另一个终端测试健康检查
curl http://127.0.0.1:18099/health
curl http://127.0.0.1:18099/mcp
curl http://127.0.0.1:18099/
# 5. 检查进程
tasklist | findstr node
netstat -ano | findstr "18099"
```
### 步骤 3: 检查依赖问题
mcp-proxy 可能缺少 Node.js 依赖:
```powershell
# 检查 mcp-proxy 的依赖
cd C:\Users\MECHREVO\.local\bin\node_modules\mcp-stdio-proxy
npm list
# 重新安装(如果有问题)
npm install -g mcp-stdio-proxy --force
```
### 步骤 4: 检查端口占用
```powershell
# 检查 18099 端口是否被占用
netstat -ano | findstr "18099"
# 如果被占用,查看占用进程
tasklist /FI "PID eq <PID>"
```
---
## 可能的失败原因
### 原因 1: Node.js 模块缺失 ⭐ 最可能
**症状**: npm 包安装成功,但 node_modules 不完整
**可能性**:
- npm 安装过程中网络问题
- 包的 postinstall 脚本失败
- Windows 长路径问题(路径超过 260 字符)
**验证**:
```powershell
# 检查 mcp-stdio-proxy 的 node_modules
dir C:\Users\MECHREVO\.local\bin\node_modules\mcp-stdio-proxy\node_modules
# 检查 package.json 中的依赖
type C:\Users\MECHREVO\.local\bin\node_modules\mcp-stdio-proxy\package.json
```
**解决**:
```powershell
# 清理并重新安装
npm uninstall -g mcp-stdio-proxy
npm cache clean --force
npm install -g mcp-stdio-proxy --verbose
```
### 原因 2: 健康检查 URL 不匹配
**症状**: mcp-proxy 启动成功,但 nuwax-agent 访问了错误的端点
**验证**: 需要查看 mcp-proxy 的实际路由
从 mcp-proxy 源码看,可能的路由:
```rust
// mcp-proxy/src/main.rs 或 server 模块
.route("/", get(root_handler))
.route("/health", get(health_check))
.route("/mcp", get(list_mcp_services)) // 这个可能不是健康检查!
```
**/mcp 可能是服务列表端点,不是健康检查!**
**正确的健康检查应该是**:
```rust
// 应该访问
http://127.0.0.1:18099/health
// 而不是
http://127.0.0.1:18099/mcp
```
### 原因 3: 环境变量问题
**症状**: mcp-proxy 需要特定的环境变量
日志显示添加了 PATH
```
Windows: 添加 npm 全局 bin 到 PATH: C:\Users\MECHREVO\AppData\Roaming\npm
```
但可能还需要其他环境变量(如果 mcp-proxy 有子进程)。
### 原因 4: 配置文件缺失
**症状**: mcp-proxy 启动时需要配置文件,但找不到
从日志中看到:
```
WARN [McpProxy] mcpServers 配置为空,跳过启动
```
**这可能是关键**!如果 `mcpServers` 配置为空mcp-proxy 可能:
- 不启动 HTTP 服务器
- 或者启动了但没有实际的服务端点
**需要确认**:
1. mcp-proxy 是否需要配置文件?
2. 配置文件应该在哪里?
3. 空配置时 mcp-proxy 的行为是什么?
### 原因 5: Windows 防火墙
**症状**: 本地回环连接被防火墙阻止
**验证**:
```powershell
# 检查防火墙规则
netsh advfirewall firewall show rule name=all | findstr "18099"
```
---
## 深入分析mcpServers 配置为空
从日志:
```
2026-02-12T06:18:32.797802Z WARN [McpProxy] mcpServers 配置为空,跳过启动
```
**这说明**
1. nuwax-agent 读取配置发现 `mcpServers` 为空
2. nuwax-agent 可能**根本没有启动 mcp-proxy 进程**
3. 或者启动了但 mcp-proxy 立即退出了
**查看完整的启动流程**
```
06:18:32.797761 INFO [BinPath] mcp-proxy 找到: C:\Users\...\mcp-proxy.cmd
06:18:32.797802 WARN [McpProxy] mcpServers 配置为空,跳过启动
06:18:32.797825 INFO [Services] MCP Proxy 启动命令已发送
06:18:41.088275 INFO [McpProxy] 可执行文件路径: C:\Users\...\mcp-proxy.cmd
06:18:41.088293 INFO [McpProxy] 监听地址: 127.0.0.1:18099
... (启动了?)
06:19:01.052725 ERROR [McpProxy] 健康检查失败
```
**矛盾点**
- 前面说"跳过启动"
- 后面又有启动日志和健康检查失败
**可能的情况**
1. **第一次启动**06:18:32被跳过了配置为空
2. **第二次启动**06:18:41实际执行了但失败了
让我查看日志中是否有多次重启:
从日志确实看到多次重启:
```
06:18:11 开始重启所有服务
06:18:32 第一次尝试启动 MCP Proxy (配置为空,跳过)
06:18:35 再次重启所有服务
06:18:40 第二次尝试启动 MCP Proxy (实际启动了)
06:19:01 健康检查失败
```
---
## 推荐的修复方案
### 方案 A: 捕获 mcp-proxy 的 stdout/stderr ⭐ 最优先
**问题**: 看不到 mcp-proxy 的实际错误
**解决**: 使用前面文档中的 `start_service_with_logging` 函数
**nuwax-agent 代码修改**
```rust
// nuwax-agent-core/src/service/mcp_proxy.rs
pub async fn start_mcp_proxy(config: &McpProxyConfig) -> Result<Child> {
let cmd_path = resolve_npm_bin("mcp-proxy").await?;
// 准备启动参数
let mut env_vars = vec![
("MCP_PORT".to_string(), config.port.to_string()),
];
// 如果有配置文件,添加环境变量
if let Some(config_path) = &config.config_file {
env_vars.push(("MCP_CONFIG".to_string(), config_path.clone()));
}
// 使用带日志捕获的启动函数
let child = crate::utils::process::start_service_with_logging(
&cmd_path,
"McpProxy",
Some(env_vars),
Some(vec!["server".to_string()]), // 子命令
).await?;
tracing::info!("[McpProxy] 进程已启动PID: {:?}", child.id());
// 健康检查(使用正确的端点)
let health_url = format!("http://127.0.0.1:{}/health", config.port);
wait_for_http_ready(&health_url, Duration::from_secs(30)).await
.context("MCP Proxy 健康检查超时")?;
Ok(child)
}
```
**效果**: 现在可以在日志中看到 mcp-proxy 的所有输出!
### 方案 B: 修复健康检查端点
**问题**: 健康检查可能用错了端点
**验证**:
1. 查看 mcp-proxy 的路由定义
2. 确认健康检查端点是 `/health` 还是 `/mcp`
**建议**: 尝试多个端点
```rust
async fn check_mcp_proxy_health(port: u16) -> Result<()> {
let endpoints = vec![
"/health",
"/",
"/mcp",
"/api/health",
];
for endpoint in &endpoints {
let url = format!("http://127.0.0.1:{}{}", port, endpoint);
tracing::debug!("Trying health check: {}", url);
match reqwest::get(&url).await {
Ok(resp) if resp.status().is_success() => {
tracing::info!("Health check succeeded: {}", url);
return Ok(());
}
Ok(resp) => {
tracing::warn!("Health check {} returned: {}", url, resp.status());
}
Err(e) => {
tracing::debug!("Health check {} failed: {}", url, e);
}
}
}
anyhow::bail!("All health check endpoints failed")
}
```
### 方案 C: 处理空配置情况
**问题**: `mcpServers` 为空时mcp-proxy 可能不应该启动
**建议**: 在 nuwax-agent 中明确处理
```rust
pub async fn start_mcp_proxy(config: &McpProxyConfig) -> Result<Option<Child>> {
// 检查配置
if config.mcp_servers.is_empty() {
tracing::info!("[McpProxy] mcpServers 配置为空,跳过启动");
return Ok(None); // 返回 None 而不是启动失败
}
// 启动服务
let child = start_service_with_logging(...).await?;
Ok(Some(child))
}
```
### 方案 D: 增加超时时间和重试
**问题**: 15 秒超时可能不够
**建议**:
```rust
// 增加超时到 30 秒
wait_for_http_ready(&health_url, Duration::from_secs(30)).await?;
// 或者添加重试逻辑
for attempt in 1..=3 {
match start_and_check_mcp_proxy(config).await {
Ok(child) => return Ok(child),
Err(e) if attempt < 3 => {
tracing::warn!("MCP Proxy 启动失败 (尝试 {}/3): {}", attempt, e);
tokio::time::sleep(Duration::from_secs(5)).await;
}
Err(e) => return Err(e),
}
}
```
---
## 需要 mcp-proxy 仓库确认的信息
为了完全解决这个问题,需要从 mcp-proxy 代码中确认:
### 1. 健康检查端点
**问题**: 正确的健康检查 URL 是什么?
**需要检查的文件**:
- `mcp-proxy/src/server.rs``mcp-proxy/src/main.rs`
- 查找 `Router``axum::Router` 配置
- 查找 `health` 相关的路由
**期望的代码**:
```rust
// mcp-proxy/src/server.rs
let app = Router::new()
.route("/health", get(health_check)) // ← 这个!
.route("/mcp", get(list_services))
// ...
```
### 2. 空配置行为
**问题**: 当没有配置 MCP 服务时mcp-proxy 会怎么做?
**可能的行为**:
1. 启动 HTTP 服务器,但没有 MCP 服务
2. 直接退出(因为没有服务可代理)
3. 报错
**需要确认**: mcp-proxy 的预期行为
### 3. 必需的环境变量
**问题**: mcp-proxy 是否需要特定的环境变量?
**需要检查**:
- 配置文件路径(`MCP_CONFIG`?
- 日志级别(`RUST_LOG`?
- 其他配置
### 4. 启动子命令
**问题**: 正确的启动命令是什么?
**可能的形式**:
```bash
mcp-proxy # 默认启动
mcp-proxy server # 明确的 server 子命令
mcp-proxy server --port 18099
mcp-proxy --config config.json server
```
---
## 立即可以做的
### 在 mcp-proxy 仓库
1. **添加详细的启动日志**
```rust
// mcp-proxy/src/main.rs
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt::init();
tracing::info!("mcp-proxy starting...");
tracing::info!("Version: {}", env!("CARGO_PKG_VERSION"));
let config = load_config()?;
tracing::info!("Configuration loaded: {:?}", config);
if config.mcp_servers.is_empty() {
tracing::warn!("No MCP servers configured");
// 决定是继续还是退出
}
let addr = SocketAddr::from(([127, 0, 0, 1], config.port));
tracing::info!("Starting HTTP server on {}", addr);
// 启动服务器...
tracing::info!("HTTP server started successfully");
Ok(())
}
```
2. **统一健康检查端点**
```rust
// 确保有一个明确的健康检查端点
.route("/health", get(|| async { "OK" }))
```
3. **添加版本信息端点**
```rust
.route("/version", get(|| async {
Json(json!({
"version": env!("CARGO_PKG_VERSION"),
"name": env!("CARGO_PKG_NAME"),
}))
}))
```
### 在 nuwax-agent 仓库
1. **立即实现日志捕获**(最优先)
2. **增加健康检查超时时间** (15s → 30s)
3. **尝试多个健康检查端点**
4. **空配置时跳过启动,不报错**
---
## 总结
| 问题 | 严重性 | 解决方案 | 优先级 |
|------|--------|----------|--------|
| **无法看到错误日志** | 🔴 高 | 捕获 stdout/stderr | P0 |
| **健康检查端点可能错误** | 🟡 中 | 尝试多个端点 | P1 |
| **空配置时行为不明** | 🟡 中 | 明确处理逻辑 | P1 |
| **超时时间太短** | 🟢 低 | 增加到 30s | P2 |
**下一步行动**:
1. ✅ 已创建完整的修复文档
2. ⏳ 需要在 Windows 上手动测试 mcp-proxy
3. ⏳ 需要确认 mcp-proxy 的健康检查端点
4. ⏳ 在 nuwax-agent 中实现日志捕获