Skip to content

MCP 客户端

MCP 客户端是实现 Model Context Protocol 的应用程序,能够连接到 MCP 服务器并利用其提供的工具、资源和提示词。本页面介绍各种可用的 MCP 客户端以及如何使用它们。

官方客户端

🤖 Claude Desktop

Claude Desktop 是 Anthropic 开发的桌面应用程序,内置了对 MCP 的原生支持。

特性

  • 原生 MCP 支持 - 无需额外配置即可使用 MCP 服务器
  • 可视化配置 - 图形界面管理 MCP 连接
  • 实时同步 - 与 Claude 网页版同步对话历史
  • 本地处理 - 支持本地 MCP 服务器连接

安装和配置

下载安装
bash
# macOS
brew install --cask claude

# Windows
# 从官网下载 .exe 安装包
# https://claude.ai/download

# Linux
# 下载 .AppImage 文件
wget https://claude.ai/download/linux/claude.AppImage
chmod +x claude.AppImage
./claude.AppImage
配置 MCP 服务器
  1. 打开 Claude Desktop
  2. 进入 设置 > MCP 服务器
  3. 添加新的服务器配置:
json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/files"]
    },
    "postgres": {
      "command": "python",
      "args": ["-m", "mcp_server_postgres"],
      "env": {
        "POSTGRES_CONNECTION_STRING": "postgresql://user:password@localhost:5432/database"
      }
    }
  }
}

使用示例

用户: 请帮我分析 /data/sales.csv 文件中的销售数据

Claude: 我来帮你分析销售数据。首先让我读取文件内容。

[调用 filesystem 服务器的 read_file 工具]

根据文件内容,我发现以下销售趋势:
1. Q4 销售额比 Q3 增长了 23%
2. 产品 A 是最畅销的产品
3. 北美地区贡献了 45% 的总销售额

需要我进行更详细的分析吗?

🔧 MCP CLI 客户端

官方命令行客户端,适合开发者和自动化场景。

安装

bash
npm install -g @modelcontextprotocol/cli

基本用法

连接服务器
bash
# 连接本地服务器
mcp connect --command "python server.py"

# 连接远程服务器
mcp connect --url "https://api.example.com/mcp"

# 使用配置文件
mcp connect --config config.json
交互模式
bash
mcp> tools list
Available tools:
- get_weather: Get weather information
- calculate: Perform calculations
- search_web: Search the internet

mcp> tools call get_weather --location "San Francisco"
{
  "content": [
    {
      "type": "text", 
      "text": "Weather in San Francisco: Sunny, 72°F"
    }
  ]
}

mcp> resources list
Available resources:
- file://config.json
- https://api.example.com/data

mcp> resources read file://config.json
{
  "contents": [
    {
      "uri": "file://config.json",
      "mimeType": "application/json",
      "text": "{\n  \"api_key\": \"...\"\n}"
    }
  ]
}
脚本模式
bash
# 执行单个命令
mcp exec --command "python server.py" --tool get_weather --args '{"location": "Tokyo"}'

# 批量执行
mcp batch --config servers.json --script commands.txt

第三方客户端

🌐 Web 客户端

MCP Web Client

基于浏览器的 MCP 客户端,支持 WebSocket 和 SSE 连接。

javascript
// 安装
npm install @mcp-community/web-client

// 使用
import { MCPWebClient } from '@mcp-community/web-client';

const client = new MCPWebClient({
  transport: 'websocket',
  url: 'wss://api.example.com/mcp'
});

await client.connect();
const tools = await client.listTools();
console.log('Available tools:', tools);

React MCP Hook

React 应用的 MCP 集成钩子。

jsx
import { useMCP } from '@mcp-community/react-hooks';

function MyComponent() {
  const { client, tools, isConnected } = useMCP({
    serverUrl: 'wss://api.example.com/mcp'
  });

  const handleToolCall = async (toolName, args) => {
    const result = await client.callTool(toolName, args);
    console.log('Tool result:', result);
  };

  return (
    <div>
      <h2>MCP Tools</h2>
      {isConnected ? (
        <ul>
          {tools.map(tool => (
            <li key={tool.name}>
              <button onClick={() => handleToolCall(tool.name, {})}>
                {tool.name}
              </button>
            </li>
          ))}
        </ul>
      ) : (
        <p>Connecting...</p>
      )}
    </div>
  );
}

📱 移动客户端

MCP Mobile (iOS/Android)

跨平台移动应用,使用 React Native 构建。

bash
# 安装
npm install -g @mcp-community/mobile-cli

# 创建新项目
mcp-mobile init MyMCPApp
cd MyMCPApp

# 运行
npm run ios     # iOS
npm run android # Android

Flutter MCP Plugin

Flutter 应用的 MCP 插件。

dart
// pubspec.yaml
dependencies:
  mcp_flutter: ^1.0.0

// main.dart
import 'package:mcp_flutter/mcp_flutter.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MCPProvider(
      serverUrl: 'wss://api.example.com/mcp',
      child: MaterialApp(
        home: MCPToolsPage(),
      ),
    );
  }
}

🖥️ 桌面客户端

Electron MCP Client

基于 Electron 的跨平台桌面客户端。

bash
# 下载预构建版本
# GitHub: https://github.com/mcp-community/electron-client

# 或从源码构建
git clone https://github.com/mcp-community/electron-client.git
cd electron-client
npm install
npm run build
npm run dist

Tauri MCP Client

使用 Rust + Web 技术构建的轻量级桌面客户端。

bash
# 安装 Tauri CLI
cargo install tauri-cli

# 克隆项目
git clone https://github.com/mcp-community/tauri-client.git
cd tauri-client

# 开发模式
cargo tauri dev

# 构建发布版
cargo tauri build

IDE 集成

🔧 VS Code 扩展

MCP for VS Code

官方 VS Code 扩展,提供 MCP 服务器的集成支持。

安装
bash
# 从 VS Code Marketplace 安装
code --install-extension modelcontextprotocol.mcp-vscode

# 或从 VSIX 文件安装
code --install-extension mcp-vscode-1.0.0.vsix
配置
json
// settings.json
{
  "mcp.servers": [
    {
      "name": "filesystem",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "${workspaceFolder}"]
    },
    {
      "name": "git",
      "command": "python",
      "args": ["-m", "mcp_server_git"],
      "cwd": "${workspaceFolder}"
    }
  ],
  "mcp.autoConnect": true,
  "mcp.showStatusBar": true
}
功能特性
  • 自动连接 - 打开项目时自动连接 MCP 服务器
  • 工具面板 - 侧边栏显示可用工具和资源
  • 命令面板 - 通过命令面板调用 MCP 功能
  • 状态栏 - 显示连接状态和服务器信息
使用示例
typescript
// 在 VS Code 中使用 MCP 工具
// 1. 打开命令面板 (Ctrl+Shift+P)
// 2. 输入 "MCP: Call Tool"
// 3. 选择工具和参数
// 4. 查看结果

// 或使用代码片段
const result = await vscode.commands.executeCommand(
  'mcp.callTool',
  'get_weather',
  { location: 'New York' }
);

🧠 IntelliJ IDEA 插件

MCP Plugin for IntelliJ

JetBrains IDE 系列的 MCP 支持插件。

安装
  1. 打开 IntelliJ IDEA
  2. 进入 File > Settings > Plugins
  3. 搜索 "Model Context Protocol"
  4. 安装并重启 IDE
配置
xml
<!-- .idea/mcp-config.xml -->
<mcp-configuration>
  <servers>
    <server name="database" command="python" args="-m mcp_server_postgres">
      <env name="DB_URL" value="postgresql://localhost:5432/mydb"/>
    </server>
    <server name="filesystem" command="npx" args="-y @modelcontextprotocol/server-filesystem ${PROJECT_DIR}"/>
  </servers>
</mcp-configuration>

📝 Vim/Neovim 插件

mcp.nvim

Neovim 的 MCP 集成插件。

lua
-- init.lua
require('mcp').setup({
  servers = {
    {
      name = 'filesystem',
      command = 'npx',
      args = {'-y', '@modelcontextprotocol/server-filesystem', vim.fn.getcwd()}
    }
  },
  auto_connect = true,
  keymaps = {
    call_tool = '<leader>mt',
    list_tools = '<leader>ml',
    read_resource = '<leader>mr'
  }
})

自定义客户端开发

🛠️ 客户端 SDK

Python SDK

python
from mcp import Client, StdioTransport

# 创建客户端
transport = StdioTransport(['python', 'server.py'])
client = Client(transport)

# 连接和初始化
await client.connect()
await client.initialize()

# 使用工具
result = await client.call_tool('get_weather', {'location': 'Paris'})
print(result.content[0].text)

# 读取资源
resource = await client.read_resource('file://data.json')
print(resource.contents[0].text)

# 断开连接
await client.disconnect()

TypeScript SDK

typescript
import { Client, StdioTransport } from '@modelcontextprotocol/sdk/client/index.js';

// 创建客户端
const transport = new StdioTransport({
  command: 'python',
  args: ['server.py']
});

const client = new Client({
  name: 'my-client',
  version: '1.0.0'
}, {
  capabilities: {
    roots: { listChanged: true }
  }
});

// 连接
await client.connect(transport);

// 列出工具
const tools = await client.listTools();
console.log('Available tools:', tools.tools);

// 调用工具
const result = await client.callTool({
  name: 'calculate',
  arguments: { expression: '2 + 2' }
});
console.log('Result:', result.content);

Go SDK

go
package main

import (
    "context"
    "fmt"
    "github.com/modelcontextprotocol/go-sdk/client"
    "github.com/modelcontextprotocol/go-sdk/transport"
)

func main() {
    // 创建传输层
    transport := transport.NewStdioTransport("python", []string{"server.py"})
    
    // 创建客户端
    client := client.New(client.Config{
        Name:    "go-client",
        Version: "1.0.0",
    })
    
    // 连接
    ctx := context.Background()
    if err := client.Connect(ctx, transport); err != nil {
        panic(err)
    }
    defer client.Disconnect()
    
    // 调用工具
    result, err := client.CallTool(ctx, "get_weather", map[string]interface{}{
        "location": "Tokyo",
    })
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("Weather: %s\n", result.Content[0].Text)
}

🔌 传输层实现

WebSocket 客户端

javascript
class WebSocketMCPClient {
  constructor(url) {
    this.url = url;
    this.ws = null;
    this.requestId = 0;
    this.pendingRequests = new Map();
  }
  
  async connect() {
    this.ws = new WebSocket(this.url);
    
    return new Promise((resolve, reject) => {
      this.ws.onopen = () => {
        this.setupMessageHandler();
        resolve();
      };
      
      this.ws.onerror = reject;
    });
  }
  
  setupMessageHandler() {
    this.ws.onmessage = (event) => {
      const message = JSON.parse(event.data);
      
      if (message.id && this.pendingRequests.has(message.id)) {
        const { resolve, reject } = this.pendingRequests.get(message.id);
        this.pendingRequests.delete(message.id);
        
        if (message.error) {
          reject(new Error(message.error.message));
        } else {
          resolve(message.result);
        }
      }
    };
  }
  
  async sendRequest(method, params = {}) {
    const id = ++this.requestId;
    const message = {
      jsonrpc: '2.0',
      method,
      params,
      id
    };
    
    return new Promise((resolve, reject) => {
      this.pendingRequests.set(id, { resolve, reject });
      this.ws.send(JSON.stringify(message));
      
      // 超时处理
      setTimeout(() => {
        if (this.pendingRequests.has(id)) {
          this.pendingRequests.delete(id);
          reject(new Error('Request timeout'));
        }
      }, 30000);
    });
  }
  
  async callTool(name, arguments) {
    return await this.sendRequest('tools/call', { name, arguments });
  }
}

SSE 客户端

javascript
class SSEMCPClient {
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
    this.eventSource = null;
    this.sessionId = null;
  }
  
  async initialize() {
    // 初始化会话
    const response = await fetch(`${this.baseUrl}/initialize`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        protocolVersion: '2025-06-18',
        capabilities: { roots: { listChanged: true } },
        clientInfo: { name: 'SSE Client', version: '1.0.0' }
      })
    });
    
    const data = await response.json();
    this.sessionId = data.sessionId;
    
    // 建立 SSE 连接
    this.eventSource = new EventSource(
      `${this.baseUrl}/events/${this.sessionId}`
    );
    
    this.eventSource.onmessage = (event) => {
      const data = JSON.parse(event.data);
      this.handleServerEvent(data);
    };
  }
  
  async callTool(name, arguments) {
    const response = await fetch(`${this.baseUrl}/tools/call`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Session-ID': this.sessionId
      },
      body: JSON.stringify({ name, arguments })
    });
    
    return await response.json();
  }
  
  handleServerEvent(data) {
    switch (data.type) {
      case 'resource_updated':
        this.onResourceUpdated(data.resource);
        break;
      case 'tool_list_changed':
        this.onToolListChanged();
        break;
    }
  }
}

客户端配置

📋 配置文件格式

JSON 配置

json
{
  "client": {
    "name": "my-mcp-client",
    "version": "1.0.0",
    "capabilities": {
      "roots": { "listChanged": true },
      "sampling": {}
    }
  },
  "servers": [
    {
      "name": "filesystem",
      "transport": {
        "type": "stdio",
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"]
      },
      "settings": {
        "timeout": 30000,
        "retries": 3
      }
    },
    {
      "name": "database",
      "transport": {
        "type": "websocket",
        "url": "wss://db.example.com/mcp"
      },
      "auth": {
        "type": "bearer",
        "token": "${DB_TOKEN}"
      }
    }
  ],
  "logging": {
    "level": "info",
    "file": "mcp-client.log"
  }
}

YAML 配置

yaml
client:
  name: my-mcp-client
  version: 1.0.0
  capabilities:
    roots:
      listChanged: true
    sampling: {}

servers:
  - name: filesystem
    transport:
      type: stdio
      command: npx
      args:
        - "-y"
        - "@modelcontextprotocol/server-filesystem"
        - "/allowed/path"
    settings:
      timeout: 30000
      retries: 3

  - name: api-server
    transport:
      type: sse
      url: https://api.example.com/mcp/sse
    auth:
      type: api-key
      key: "${API_KEY}"

logging:
  level: info
  file: mcp-client.log

🔐 认证配置

API 密钥认证

json
{
  "auth": {
    "type": "api-key",
    "key": "${API_KEY}",
    "header": "X-API-Key"
  }
}

Bearer Token 认证

json
{
  "auth": {
    "type": "bearer",
    "token": "${ACCESS_TOKEN}"
  }
}

OAuth 2.0 认证

json
{
  "auth": {
    "type": "oauth2",
    "clientId": "${OAUTH_CLIENT_ID}",
    "clientSecret": "${OAUTH_CLIENT_SECRET}",
    "tokenUrl": "https://auth.example.com/token",
    "scopes": ["mcp:read", "mcp:write"]
  }
}

最佳实践

🚀 性能优化

连接池管理

javascript
class MCPConnectionPool {
  constructor(config) {
    this.config = config;
    this.connections = new Map();
    this.maxConnections = config.maxConnections || 10;
  }
  
  async getConnection(serverName) {
    if (this.connections.has(serverName)) {
      return this.connections.get(serverName);
    }
    
    if (this.connections.size >= this.maxConnections) {
      // 移除最久未使用的连接
      const oldestKey = this.connections.keys().next().value;
      const oldestConnection = this.connections.get(oldestKey);
      await oldestConnection.disconnect();
      this.connections.delete(oldestKey);
    }
    
    const connection = await this.createConnection(serverName);
    this.connections.set(serverName, connection);
    return connection;
  }
  
  async createConnection(serverName) {
    const serverConfig = this.config.servers[serverName];
    const client = new MCPClient(serverConfig);
    await client.connect();
    return client;
  }
}

请求缓存

javascript
class CachedMCPClient {
  constructor(client, cacheConfig = {}) {
    this.client = client;
    this.cache = new Map();
    this.cacheTTL = cacheConfig.ttl || 300000; // 5分钟
  }
  
  async callTool(name, arguments) {
    const cacheKey = `${name}:${JSON.stringify(arguments)}`;
    const cached = this.cache.get(cacheKey);
    
    if (cached && Date.now() - cached.timestamp < this.cacheTTL) {
      return cached.result;
    }
    
    const result = await this.client.callTool(name, arguments);
    this.cache.set(cacheKey, {
      result,
      timestamp: Date.now()
    });
    
    return result;
  }
}

🛡️ 错误处理

重试机制

javascript
class ResilientMCPClient {
  constructor(client, retryConfig = {}) {
    this.client = client;
    this.maxRetries = retryConfig.maxRetries || 3;
    this.retryDelay = retryConfig.retryDelay || 1000;
  }
  
  async callToolWithRetry(name, arguments) {
    let lastError;
    
    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      try {
        return await this.client.callTool(name, arguments);
      } catch (error) {
        lastError = error;
        
        if (attempt < this.maxRetries) {
          const delay = this.retryDelay * Math.pow(2, attempt);
          await new Promise(resolve => setTimeout(resolve, delay));
        }
      }
    }
    
    throw lastError;
  }
}

断路器模式

javascript
class CircuitBreakerMCPClient {
  constructor(client, config = {}) {
    this.client = client;
    this.failureThreshold = config.failureThreshold || 5;
    this.resetTimeout = config.resetTimeout || 60000;
    this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
    this.failureCount = 0;
    this.lastFailureTime = null;
  }
  
  async callTool(name, arguments) {
    if (this.state === 'OPEN') {
      if (Date.now() - this.lastFailureTime > this.resetTimeout) {
        this.state = 'HALF_OPEN';
      } else {
        throw new Error('Circuit breaker is OPEN');
      }
    }
    
    try {
      const result = await this.client.callTool(name, arguments);
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }
  
  onSuccess() {
    this.failureCount = 0;
    this.state = 'CLOSED';
  }
  
  onFailure() {
    this.failureCount++;
    this.lastFailureTime = Date.now();
    
    if (this.failureCount >= this.failureThreshold) {
      this.state = 'OPEN';
    }
  }
}

相关资源

🚀 探索模型上下文协议的无限可能 | 连接 AI 与世界的桥梁 | 让智能更智能