MCP testing locally: Claude Desktop Connection

Claude Desktop Connection is the process of configuring Anthropic’s Claude desktop application to communicate with local MCP (Model Context Protocol) servers via stdio (standard input/output) transport.

CN: Claude Desktop 连接是指将 Anthropic 的 Claude 桌面应用程序配置为通过 stdio(标准输入/输出)传输方式与本地 MCP(模型上下文协议)服务器通信的过程。

Prerequisites

ENCN
Claude Desktop installed (latest version)Claude Desktop 已安装(最新版本)
Node.js installed (for npx)Node.js 已安装(用于 npx)
Your MCP server code ready (Python/Node.js)你的 MCP 服务器代码已就绪(Python/Node.js)
Config file accessible配置文件可访问
Config File Locations
OSPathENCN
macOS~/Library/Application Support/Claude/claude_desktop_config.jsonUser Library folder用户资料库文件夹
Windows%APPDATA%\Claude\claude_desktop_config.jsonAppData Roaming folderAppData Roaming 文件夹
Linux~/.config/Claude/claude_desktop_config.jsonConfig directory配置目录

Configuration Structure

The config file uses a JSON object with a top-level mcpServers key. Each server is a nested object with:

  • command: The executable to run (e.g., "python""npx""node")
  • args: Array of arguments passed to the command
  • cwd (optional): Working directory for the server process
  • env (optional): Environment variables

CN: 配置文件使用一个带有顶层 mcpServers 键的 JSON 对象。每个服务器是一个嵌套对象,包含:

  • command: 要运行的可执行文件(如 "python""npx""node"
  • args: 传递给命令的参数数组
  • cwd(可选): 服务器进程的工作目录
  • env(可选): 环境变量

Three Common Config Patterns

Pattern 1 — Python Server
{
  "mcpServers": {
    "my-python-server": {
      "command": "python",
      "args": ["/path/to/your/mcp_server.py"],
      "cwd": "/path/to/your/project"
    }
  }
}
Pattern 2 — npx Server
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/username/Desktop"]
    }
  }
}
Pattern 3 — Remote Bridge
{
  "mcpServers": {
    "remote-server": {
      "command": "npx",
      "args": ["mcp-remote", "http://studio:3456/sse"]
    }
  }
}

Complete Setup & Testing

Step 1: Verify Prerequisites

# EN: Check Node.js installation / 检查 Node.js 安装
node --version
# EN: Should output v18+ / 应输出 v18+

# EN: Check npm installation / 检查 npm 安装
npm --version

# EN: Check Python installation (if using Python server) / 检查 Python 安装(如果用 Python 服务器)
python --version
# EN: Should output Python 3.11+ / 应输出 Python 3.11+

Step 2: Create/Edit Config File (Windows Example)

Bash
C:

CD C:\Users\chene\AppData\Roaming\Claude        # 如果没有建立一个

create a file, named: claude_desktop_config.json (content below)

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "C:\\Users\\yourusername\\Desktop",
        "C:\\Users\\yourusername\\Downloads"
      ]
    },
    "my-python-mcp": {
      "command": "python",
      "args": [
        "C:\\Users\\yourusername\\projects\\my-mcp-server\\mcp_server.py"
      ],
      "cwd": "C:\\Users\\yourusername\\projects\\my-mcp-server"
    }
  }
}

Create mcp server

according to my config file above, create a mcp_seerver.py at
 "C:\\Users\\yourusername\\projects\\my-mcp-server\\mcp_server.py"

#!/usr/bin/env python3
"""
EN: Complete MCP Server with proper handshake (initialize + tools/list + tools/call).
CN: 完整的 MCP 服务器,包含正确的握手(initialize + tools/list + tools/call)。
"""

import json
import sys


def main():
    """
    EN: Main loop: reads JSON-RPC lines from stdin, writes responses to stdout.
    CN: 主循环:从 stdin 读取 JSON-RPC 行,向 stdout 写入响应。
    """
    # EN: MCP uses stdio — keep reading line by line forever
    # CN: MCP 使用 stdio —— 永远逐行持续读取
    for line in sys.stdin:
        # EN: Remove trailing newline and skip empty lines
        # CN: 移除末尾换行符并跳过空行
        line = line.strip()
        if not line:
            continue

        try:
            # EN: Parse the incoming JSON-RPC request
            # CN: 解析传入的 JSON-RPC 请求
            request = json.loads(line)
            method = request.get("method")
            req_id = request.get("id")

            # ============================================================
            # EN: PART 1 — MCP Handshake (MUST handle "initialize")
            # CN: 第一部分 — MCP 握手(必须处理 "initialize")
            # ============================================================
            if method == "initialize":
                """
                EN: Respond to the initialize request with protocol version and capabilities.
                    Then immediately send the "initialized" notification.
                    This is REQUIRED for Claude Desktop to accept the connection.
                CN: 用协议版本和能力响应 initialize 请求。
                    然后立即发送 "initialized" 通知。
                    这是 Claude Desktop 接受连接所必需的。
                """
                # EN: Send the initialize response back to Claude Desktop
                # CN: 将 initialize 响应发送回 Claude Desktop
                response = {
                    "jsonrpc": "2.0",
                    "id": req_id,
                    "result": {
                        "protocolVersion": "2024-11-05",
                        "capabilities": {
                            "tools": {}  # EN: We support tools / CN: 我们支持工具
                        },
                        "serverInfo": {
                            "name": "my-python-mcp",
                            "version": "1.0.0"
                        }
                    }
                }
                sys.stdout.write(json.dumps(response) + "\n")
                sys.stdout.flush()

                # EN: Send the "initialized" notification (THIS IS CRITICAL)
                # CN: 发送 "initialized" 通知(这是关键)
                notification = {
                    "jsonrpc": "2.0",
                    "method": "notifications/initialized"
                }
                sys.stdout.write(json.dumps(notification) + "\n")
                sys.stdout.flush()

            # ============================================================
            # EN: PART 2 — List available tools
            # CN: 第二部分 — 列出可用工具
            # ============================================================
            elif method == "tools/list":
                """
                EN: Return the list of tools this server provides.
                CN: 返回此服务器提供的工具列表。
                """
                response = {
                    "jsonrpc": "2.0",
                    "id": req_id,
                    "result": {
                        "tools": [
                            {
                                "name": "hello",
                                "description": "Say hello to someone",
                                "inputSchema": {
                                    "type": "object",
                                    "properties": {
                                        "name": {
                                            "type": "string",
                                            "description": "Name to greet"
                                        }
                                    },
                                    "required": ["name"]
                                }
                            }
                        ]
                    }
                }
                sys.stdout.write(json.dumps(response) + "\n")
                sys.stdout.flush()

            # ============================================================
            # EN: PART 3 — Execute a tool
            # CN: 第三部分 — 执行工具
            # ============================================================
            elif method == "tools/call":
                """
                EN: Execute the requested tool with the given arguments.
                CN: 用给定的参数执行请求的工具。
                """
                params = request.get("params", {})
                tool_name = params.get("name")
                arguments = params.get("arguments", {})

                if tool_name == "hello":
                    name = arguments.get("name", "World")
                    result = {
                        "jsonrpc": "2.0",
                        "id": req_id,
                        "result": {
                            "content": [
                                {
                                    "type": "text",
                                    "text": f"Hello, {name}! 👋"
                                }
                            ]
                        }
                    }
                    sys.stdout.write(json.dumps(result) + "\n")
                    sys.stdout.flush()

        except json.JSONDecodeError as e:
            # EN: Handle invalid JSON gracefully
            # CN: 优雅地处理无效 JSON
            error_response = {
                "jsonrpc": "2.0",
                "id": None,
                "error": {
                    "code": -32700,
                    "message": f"Parse error: {e}"
                }
            }
            sys.stdout.write(json.dumps(error_response) + "\n")
            sys.stdout.flush()
        except Exception as e:
            # EN: Catch any other error to prevent the server from crashing silently
            # CN: 捕获任何其他错误,防止服务器静默崩溃
            error_response = {
                "jsonrpc": "2.0",
                "id": request.get("id") if 'request' in locals() else None,
                "error": {
                    "code": -32603,
                    "message": f"Internal error: {e}"
                }
            }
            sys.stdout.write(json.dumps(error_response) + "\n")
            sys.stdout.flush()


if __name__ == "__main__":
    main()

Step 3: Validate JSON

A trailing comma will silently break the configuration — Claude Desktop won’t show errors, tools just won’t appear.

CN: 尾随逗号会静默破坏配置——Claude Desktop 不会显示错误,工具就是出不来

Bash

c:
cd  C:\Users\chene\AppData\Roaming\Claude


# EN: Validate JSON using Python / 用 Python 验证 JSON
python -c "import json; json.load(open('claude_desktop_config.json'))"
# EN: No output means valid JSON / 无输出表示 JSON 有效

# EN: Or use online validator / 或使用在线验证器
# https://jsonlint.com/

Step 4: Restart Claude Desktop

 Critical: You must fully quit Claude Desktop and reopen it. A window reload or “Refresh” is NOT enough.

CN — 关键: 你必须完全退出 Claude Desktop 再重新打开。窗口刷新或”重新加载”是不够的

PlatformHow to fully quitENCN
macOSCmd + Q or right-click dock icon → QuitCommand+Q 或右键 Dock 图标→退出Command+Q 或右键 Dock 图标→退出
WindowsRight-click system tray → Exit右键系统托盘→退出右键系统托盘→退出