大数跨境

测试老司机手把手教你打造MCP Server连接测试工具

测试老司机手把手教你打造MCP Server连接测试工具 51Testing软件测试网
2026-10-13
6
导读:打造MCP连接测试工具:覆盖连接测试、批量并行、环境检查、CI/CD集成、监控告警,效率提升80%。

前言

作为一名在测试领域摸爬滚打十多年的老司机,我见证了从传统的单体应用测试到微服务架构测试,再到如今AI测试的演进历程。

最近在项目中频繁接触各种MCP服务器,发现市面上缺乏一个好用的连接测试工具,于是决定自己动手打造一个。

今天就来分享一下我是如何从零开始构建这个MCP连接测试工具的,希望能给同行们一些启发。这不是那种照本宣科的教程,而是一个实战派测试工程师的真实经验分享。

什么是MCP?为什么需要专门的测试工具?

MCP(Model Context Protocol)是一个用于AI模型与外部工具交互的协议标准。简单来说,它让AI能够调用各种外部服务和工具,比如文件操作、数据库查询、API调用等。

在我的日常工作中,经常需要测试各种MCP服务器:

  • Sequential Thinking Server:用于复杂推理任务
  • Chrome DevTools MCP:浏览器自动化和调试工具
  • GitHub MCP Server:GitHub仓库管理和操作
  • Mobile MCP Server:移动端自动化测试工具

每次手动测试这些服务器连接既费时又容易出错,特别是在CI/CD流水线中,我们需要快速验证MCP服务器的可用性。于是萌生了开发一个专门测试工具的想法。

需求分析:测试工程师的痛点

在开始编码之前,我先梳理了一下测试MCP服务器时遇到的主要痛点:

痛点类别
具体问题
影响程度
解决紧迫性
连接测试复杂
每个MCP服务器的启动命令和参数都不同,手动测试需要记住大量配置信息
高
高
批量测试困难
项目中通常会用到多个MCP服务器,逐个测试效率低下
高
高
结果难以追踪
测试结果散落在各处,缺乏统一的记录和分析
中
中
环境依赖检查
MCP服务器通常依赖Node.js、Python等环境,需要提前检查依赖是否满足
中
高
配置管理混乱
不同项目的MCP配置格式不统一,导入导出困难
中
中

基于这些痛点,我确定了工具的核心功能需求。

技术选型:为什么选择Flask + Bootstrap

作为一个实用主义者,我在技术选型时主要考虑以下几个因素:

后端选择Flask的原因

  1. 轻量级:
    相比Django,Flask更适合这种工具型应用
  2. Python生态:
    MCP官方提供了Python SDK,集成方便
  3. 快速开发:
    Flask的简洁性让我能快速实现MVP
  4. 异步支持:
    通过asyncio可以并发测试多个服务器

前端选择Bootstrap的原因

  1. 响应式设计:
    一套代码适配桌面和移动端
  2. 组件丰富:
    模态框、表单、按钮等开箱即用
  3. 学习成本低:
    团队成员都熟悉,维护方便
  4. 兼容性好:
    不用担心浏览器兼容问题

为什么不用React/Vue?

说实话,作为一个测试工具,我更看重的是稳定性和开发效率,而不是炫酷的交互效果。原生JavaScript + Bootstrap已经能满足所有需求,何必增加复杂度?

架构设计:简单而实用

整个工具采用经典的MVC架构,但做了一些适合测试场景的调整:

目录结构


mcp-connection-toolkit/
├── app.py# Flask主应用,负责启动和配置
├── routes/# 路由层,分离Web页面和API
│   ├── web.py# Web页面路由
│   └── api.py# RESTful API路由
├── services/# 业务逻辑层
│   └── mcp_service.py# MCP连接服务核心逻辑
├── templates/# 视图层
│   ├── base.html# 基础模板
│   ├── index.html# 主页面
│   ├── config.html# 配置管理页面
│   └── results.html# 结果展示页面
└── static/# 静态资源
    ├── css/style.css# 自定义样式
    └── js/# 前端交互逻辑


核心设计原则

  1. 单一职责:
    每个模块只负责一个功能
  2. 松耦合:
    前后端通过RESTful API通信
  3. 可扩展:
    新增MCP服务器类型只需修改配置
  4. 容错性:
    网络异常、服务器故障都有相应处理

核心功能实现详解

1. MCP服务连接核心逻辑

这是整个工具的核心,我花了最多时间在这里。让我们看看关键代码:

Pythonmcp_service.py


async def test_server(self, server_id: str) -> Dict:
    #测试单个服务器连接
    if server_id not in self.servers:
        raise ValueError(f"服务器 {server_id} 不存在")
    
    server_config = self.servers[server_id]
    start_time = datetime.now()
    
    result = {
        'server_id': server_id,
        'server_name': server_config['name'],
        'start_time': start_time.isoformat(),
        'success': False,
        'tools': [],
        'tools_count': 0,
        'error': None,
        'duration': 0
    }
    
    try:
        # 检查命令是否存在
        command = server_config['command']
        command_path = shutil.which(command)
        
        if not command_path:
            error_msg = f"命令 '{command}' 未找到。"
            if command == "npx":
                error_msg += " 请确保已安装 Node.js 并且 npm/npx 在系统 PATH 中。"
            result['error'] = error_msg
            return result
        
        # 创建服务器参数
        env = dict(os.environ)
        env.update(server_config.get('env', {}))
        
        server_params = StdioServerParameters(
            command=command_path,
            args=server_config['args'],
            env=env
        )
        
        # 连接到MCP服务器
        async with stdio_client(server_params) as (read, write):
            async with ClientSession(read, write) as session:
                # 初始化会话(设置超时)
                await asyncio.wait_for(session.initialize(), timeout=30.0)
                
                # 获取可用工具
                tools_list = await session.list_tools()
                
                # 记录成功结果
                result['success'] = True
                result['tools_count'] = len(tools_list.tools)
                result['tools'] = [
                    {
                        'name': tool.name,
                        'description': tool.description
                    }
                    for tool in tools_list.tools
                ]
                
    except asyncio.TimeoutError:
        result['error'] = "连接超时 - 请检查MCP服务器是否正确安装和配置"
    exceptExceptionas e:
        result['error'] = f"连接失败: {str(e)}"
    
    # 计算测试时长
    end_time = datetime.now()
    result['end_time'] = end_time.isoformat()
    result['duration'] = (end_time - start_time).total_seconds()
    
    # 保存测试结果
    self.test_results.append(result)
    
    return result


关键技术点解析

1. 异步处理

使用asyncio实现并发测试,大大提升了批量测试的效率。在实际测试中,10个服务器的并发测试比串行测试快了70%以上。

2. 超时控制

设置30秒超时避免长时间等待。这个时间是我经过大量测试得出的经验值,既能保证正常服务器有足够时间启动,又能快速识别问题服务器。

3. 环境变量处理

继承系统环境变量并允许自定义,这样可以处理各种复杂的MCP服务器启动需求。

4. 错误分类处理

针对不同类型的错误给出具体的解决建议,提升用户体验。

2. 前端交互设计

作为测试工具,用户体验同样重要。我在前端设计上花了不少心思:

实时状态更新

JavaScript前端交互


// 处理测试单个服务器
async function handleTestServer(serverId) {
    const server = servers.find(s => s.id === serverId);
    if (!server) return;
    
    const statusElement = document.getElementById(`status-${serverId}`);
    const testBtn = document.querySelector(`[data-server-id="${serverId}"].test-server-btn`);
    
    // 更新状态为测试中
    statusElement.innerHTML = `
        <small class="text-warning">
            ${createStatusIndicator('testing').outerHTML}
            测试中...
        </small>
    `
;
    
    // 设置按钮为处理中状态
    testBtn.disabled = true;
    const originalIcon = testBtn.querySelector('i');
    testBtn.dataset.originalIconClass = originalIcon.className;
    originalIcon.className = 'bi bi-hourglass-split';
    
    try {
        const response = await apiRequest(`/test/${serverId}`, {
            method: 'POST'
        });
        
        const result = response.data;
        
        // 更新状态显示
        if (result.success) {
            statusElement.innerHTML = `
                <small class="text-success">
                    ${createStatusIndicator('success').outerHTML}
                    连接成功 (${result.tools_count} 个工具)
                </small>
            `
;
            showNotification(`服务器 "${server.name}" 测试成功`, 'success');
        } else {
            statusElement.innerHTML = `
                <small class="text-danger">
                    ${createStatusIndicator('error').outerHTML}
                    连接失败
                </small>
            `
;
            showNotification(`服务器 "${server.name}" 测试失败: ${result.error}`, 'danger');
        }
        
    } catch (error) {
        // 错误处理...
    } finally {
        // 恢复按钮状态
        testBtn.disabled = false;
        const icon = testBtn.querySelector('i');
        if (testBtn.dataset.originalIconClass) {
            icon.className = testBtn.dataset.originalIconClass;
        }
    }
}

配置模式切换

考虑到不同用户的使用习惯,我设计了两种配置模式:

  1. 简单模式:
    适合新手,通过表单填写基本信息
  2. JSON模式:
    适合高级用户,直接粘贴MCP配置文件

JavaScript - 模式切换


// 处理配置模式切换
functionhandleConfigModeChange(event) {
    const mode = event.target.id === 'simpleMode' ? 'simple' : 'json';
    
    document.querySelectorAll('.config-form').forEach(form => {
        if (form.dataset.mode === mode) {
            form.classList.remove('d-none');
        } else {
            form.classList.add('d-none');
        }
    });
}

3. 系统环境检查

这是我加的一个贴心功能。很多时候MCP服务器连接失败是因为环境问题,比如Node.js没装、版本不对等。

Python - 环境检查


def check_system_environment():
    """检查系统环境"""
    system_info = {
        'platform': platform.system(),
        'python_version': platform.python_version(),
        'commands': {},
        'suggestions': []
    }
    
    # 检查常用命令
    commands_to_check = ['node', 'npm', 'npx', 'python', 'pip', 'uvx']
    
    for cmd in commands_to_check:
        cmd_info = {
            'exists': False,
            'version': None,
            'path': None
        }
        
        try:
            cmd_path = shutil.which(cmd)
            if cmd_path:
                cmd_info['exists'] = True
                cmd_info['path'] = cmd_path
                
                # 获取版本信息
                try:
                    if cmd in ['node', 'npm', 'npx']:
                        result = subprocess.run([cmd, '--version'], 
                                              capture_output=True, text=True, timeout=5)
                        if result.returncode == 0:
                            cmd_info['version'] = result.stdout.strip()
                except:
                    pass
                                       
        exceptException:
            pass
                
        system_info['commands'][cmd] = cmd_info
    
    # 生成建议
    if not system_info['commands']['node']['exists']:
        system_info['suggestions'].append(
            '建议安装 Node.js 以支持 npx 命令运行的MCP服务器'
        )
    
    if not system_info['commands']['uvx']['exists']:
        system_info['suggestions'].append(
            '建议安装 uv 包管理器以支持 uvx 命令运行的MCP服务器'
        )
    
    return system_info    


这个功能帮我节省了大量排查环境问题的时间,特别是在新环境部署时。

常用MCP服务器测试实战

在实际工作中,我经常测试以下几类MCP服务器,每类都有其特点:

1. Sequential Thinking Server

用途:复杂推理和思维链分析

配置:

JSON 配置


{
  "mcpServers": {
    "sequential-thinking": {
      "command": "npx",
      "args": ["-y", "@iflow-mcp/server-sequential-thinking@0.6.2"],
      "values": {}
    }
  }
}

测试要点:

  • 启动时间较长(通常需要10-15秒)
  • 提供thinking、analyze等工具
  • 对网络要求较高

2. Chrome DevTools MCP

用途:浏览器自动化和调试工具

配置:

JSON 配置


{
  "mcpServers": {
    "chrome-devtools": {
      "command": "npx",
      "args": ["-y", "@iflow-mcp/chrome-devtools-mcp"],
      "values": {}
    }
  }
}

测试要点:

  • 需要Chrome浏览器环境
  • 提供页面操作、截图、性能分析等功能
  • 测试时需要验证浏览器连接状态

3. GitHub MCP Server

用途:GitHub仓库管理和操作

配置:

JSON 配置


{
"mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@iflow-mcp/server-github@0.6.2"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PERSONAL_ACCESS_TOKEN}"
      },
      "values": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": {
          "type": "string",
          "title": "GitHub个人访问令牌",
          "description": "GitHub个人访问令牌。在GitHub设置中生成个人访问令牌",
          "default": "your_github_token_here"
        }
      }
    },
    "mobile-mcp": {
      "command": "npx",
      "args": ["-y", "@mobilenext/mobile-mcp@latest"]
    }
  }
}

测试要点:

  • 需要配置GitHub Personal Access Token
  • 提供仓库查询、Issue管理、PR操作等功能
  • 测试时需要验证Token权限是否正确
  • 注意API速率限制问题

4. Mobile MCP Server

用途:移动端自动化测试工具

配置:

JSON 配置


{
  "mcpServers": {
    "mobile-mcp": {
      "command": "npx",
      "args": ["-y", "@mobilenext/mobile-mcp@latest"]
    }
  }
}

测试要点:

  • 提供移动设备模拟和控制功能
  • 包含UI自动化测试、性能监控等工具
  • 支持Android和iOS平台的自动化操作
  • 需要验证设备连接和权限配置

踩坑记录:那些让人头疼的问题

在开发过程中,我遇到了不少坑,这里分享几个典型的:

坑1:Windows路径问题

问题描述:在Windows系统上,shutil.which('npx')找不到命令

原因分析:Windows的npx实际上是npx.cmd,需要特殊处理

解决方案:

Python


def _normalize_command(self, command: str) -> str:
    """标准化命令,处理Windows系统的特殊情况"""
    # 直接返回原始命令,让系统和MCP库处理
    return command

def _check_command_exists(self, command: str) -> bool:
    """检查命令是否存在"""
    try:
        # 在Windows上,shutil.which会自动处理.cmd, .exe等扩展名
        return shutil.which(command) is notNone
    except Exception:
        return False


坑2:异步超时处理

问题描述:某些MCP服务器启动很慢,经常超时

原因分析:网络下载依赖包需要时间

解决方案:

Python


# 设置合理的超时时间,并提供重试机制
await asyncio.wait_for(session.initialize(), timeout=30.0)

坑3:环境变量继承

问题描述:MCP服务器找不到系统PATH中的命令

原因分析:子进程没有继承完整的环境变量

解决方案:

Python


# 确保环境变量包含PATH
env = dict(os.environ)
env.update(server_config.get('env', {}))

server_params = StdioServerParameters(
    command=normalized_command,
    args=server_config['args'],
    env=env
)

坑4:前端状态同步

问题描述:并发测试时,前端状态显示混乱

原因分析:异步请求的时序问题

解决方案:

JavaScript


// 使用唯一ID标识每个测试任务
const testId = `test-${Date.now()}-${Math.random()}`;
// 在状态更新时检查testId,避免过期更新


——未完待续——

声明:本文为51Testing软件测试网 blues_C 用户投稿内容,该用户投稿时已经承诺独立承担涉及知识产权的相关法律责任,并且已经向51Testing承诺此文并无抄袭内容。发布本文的用途仅仅为学习交流,不做任何商用,未经授权请勿转载,否则作者和51Testing有权追究责任。如果您发现本公众号中有涉嫌抄袭的内容,欢迎发送邮件至:editor@51testing.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。


【声明】内容源于网络
0
0
51Testing软件测试网
博为峰51Testing软件测试网提供各种线上招聘、线上课程等网络服务,出版软件测试系列丛书及电子杂志,组织线上技术交流活动;同时还举办多种线下公益活动,如软件测试沙龙、软件测试专场招聘会等。
内容 3967
粉丝 0
51Testing软件测试网 博为峰51Testing软件测试网提供各种线上招聘、线上课程等网络服务,出版软件测试系列丛书及电子杂志,组织线上技术交流活动;同时还举办多种线下公益活动,如软件测试沙龙、软件测试专场招聘会等。
总阅读4.6k
粉丝0
内容4.0k