Skip to content

客户端开发(Node)

系统要求

在开始之前,请确保您的系统满足以下要求:

  • Mac或Windows电脑 安装Node.js 16或更高版本
  • 安装最新版本的npm Anthropic API密钥(Claude)

设置您的环境

首先,让我们创建并设置我们的项目:

bash
# Create project directory
mkdir mcp-client-typescript
cd mcp-client-typescript

# Initialize npm project
npm init -y

# Install dependencies
npm install @anthropic-ai/sdk @modelcontextprotocol/sdk dotenv

# Install dev dependencies
npm install -D @types/node typescript

# Create source file
touch index.ts
bash
# Create project directory
md mcp-client-typescript
cd mcp-client-typescript

# Initialize npm project
npm init -y

# Install dependencies
npm install @anthropic-ai/sdk @modelcontextprotocol/sdk dotenv

# Install dev dependencies
npm install -D @types/node typescript

# Create source file
new-item index.ts

更新你的 package.json 文件,将 type 设置为 'module' 并添加一个构建脚本:

json
{
  "type": "module",
  "scripts": {
    "build": "tsc && chmod 755 build/index.js"
  }
}

Create a tsconfig.json in the root of your project:

json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["index.ts"],
  "exclude": ["node_modules"]
}

设置您的API密钥

您需要从Anthropic控制台获取一个Anthropic API密钥。

创建一个.env文件来存储它:

bash
echo "ANTHROPIC_API_KEY=<your key here>" > .env

Add .env to your .gitignore:

bash
echo ".env" >> .gitignore

注意

确保您的 ANTHROPIC_API_KEY 安全!

创建客户端

基本客户端结构

首先,让我们设置导入并在index.ts中创建基本的客户端类:

typescript
import { Anthropic } from "@anthropic-ai/sdk";
import {
  MessageParam,
  Tool,
} from "@anthropic-ai/sdk/resources/messages/messages.mjs";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import readline from "readline/promises";
import dotenv from "dotenv";

dotenv.config();

const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
if (!ANTHROPIC_API_KEY) {
  throw new Error("ANTHROPIC_API_KEY is not set");
}

class MCPClient {
  private mcp: Client;
  private anthropic: Anthropic;
  private transport: StdioClientTransport | null = null;
  private tools: Tool[] = [];

  constructor() {
    this.anthropic = new Anthropic({
      apiKey: ANTHROPIC_API_KEY,
    });
    this.mcp = new Client({ name: "mcp-client-cli", version: "1.0.0" });
  }
  // methods will go here
}

服务器连接管理

接下来,我们将实现连接到MCP服务器的方法:

typescript
async connectToServer(serverScriptPath: string) {
  try {
    const isJs = serverScriptPath.endsWith(".js");
    const isPy = serverScriptPath.endsWith(".py");
    if (!isJs && !isPy) {
      throw new Error("Server script must be a .js or .py file");
    }
    const command = isPy
      ? process.platform === "win32"
        ? "python"
        : "python3"
      : process.execPath;
    
    this.transport = new StdioClientTransport({
      command,
      args: [serverScriptPath],
    });
    this.mcp.connect(this.transport);
    
    const toolsResult = await this.mcp.listTools();
    this.tools = toolsResult.tools.map((tool) => {
      return {
        name: tool.name,
        description: tool.description,
        input_schema: tool.inputSchema,
      };
    });
    console.log(
      "Connected to server with tools:",
      this.tools.map(({ name }) => name)
    );
  } catch (e) {
    console.log("Failed to connect to MCP server: ", e);
    throw e;
  }
}

查询处理逻辑

现在让我们添加用于处理查询和工具调用的核心功能:

typescript
async processQuery(query: string) {
  const messages: MessageParam[] = [
    {
      role: "user",
      content: query,
    },
  ];

  const response = await this.anthropic.messages.create({
    model: "claude-3-5-sonnet-20241022",
    max_tokens: 1000,
    messages,
    tools: this.tools,
  });

  const finalText = [];
  const toolResults = [];

  for (const content of response.content) {
    if (content.type === "text") {
      finalText.push(content.text);
    } else if (content.type === "tool_use") {
      const toolName = content.name;
      const toolArgs = content.input as { [x: string]: unknown } | undefined;

      const result = await this.mcp.callTool({
        name: toolName,
        arguments: toolArgs,
      });
      toolResults.push(result);
      finalText.push(
        `[Calling tool ${toolName} with args ${JSON.stringify(toolArgs)}]`
      );

      messages.push({
        role: "user",
        content: result.content as string,
      });

      const response = await this.anthropic.messages.create({
        model: "claude-3-5-sonnet-20241022",
        max_tokens: 1000,
        messages,
      });

      finalText.push(
        response.content[0].type === "text" ? response.content[0].text : ""
      );
    }
  }

  return finalText.join("\n");
}

交互式聊天界面

现在我们将添加聊天循环和清理功能:

typescript
async chatLoop() {
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });

  try {
    console.log("\nMCP Client Started!");
    console.log("Type your queries or 'quit' to exit.");

    while (true) {
      const message = await rl.question("\nQuery: ");
      if (message.toLowerCase() === "quit") {
        break;
      }
      const response = await this.processQuery(message);
      console.log("\n" + response);
    }
  } finally {
    rl.close();
  }
}

async cleanup() {
  await this.mcp.close();
}

主入口点

最后,我们将添加主执行逻辑:

typescript
async function main() {
  if (process.argv.length < 3) {
    console.log("Usage: node index.ts <path_to_server_script>");
    return;
  }
  const mcpClient = new MCPClient();
  try {
    await mcpClient.connectToServer(process.argv[2]);
    await mcpClient.chatLoop();
  } finally {
    await mcpClient.cleanup();
    process.exit(0);
  }
}

main();

运行客户端

要使用任何MCP服务器运行您的客户端:

bash
# Build TypeScript
npm run build

# Run the client
node build/index.js path/to/server.py # python server
node build/index.js path/to/build/index.js # node server

注意

如果您正在继续服务器快速入门中的天气示例,您的命令可能看起来像这样:node build/index.js .../quickstart-resources/weather-server-typescript/build/index.js

客户端将会:

  1. 连接到指定的服务器
  2. 列出可用工具
  3. 启动交互式聊天会话,您可以:
    • 输入查询
    • 查看工具执行情况
    • 获取来自Claude的响应

它是如何工作的

当您提交查询时:

  1. 客户端从服务器获取可用工具列表
  2. 您的查询与工具描述一起发送到Claude
  3. Claude决定使用哪些工具(如果有的话)
  4. 客户端通过服务器执行任何请求的工具调用
  5. 结果被发送回Claude
  6. Claude提供自然语言响应
  7. 响应显示给您

最佳实践

1.错误处理

  • 使用TypeScript的类型系统以获得更好的错误检测
  • 将工具调用包装在try-catch块中
  • 提供有意义的错误消息
  • 优雅地处理连接问题

2.安全性

  • 在.env中安全地存储API密钥
  • 验证服务器响应
  • 谨慎处理工具权限

故障排除

服务器路径问题

  • 仔细检查服务器脚本的路径是否正确
  • 如果相对路径不起作用,请使用绝对路径
  • 对于Windows用户,确保在路径中使用正斜杠(/)或转义的反斜杠()
  • 验证服务器文件具有正确的扩展名(Node.js用.js,Python用.py)

正确路径使用示例:

bash
# Relative path
node build/index.js ./server/build/index.js

# Absolute path
node build/index.js /Users/username/projects/mcp-server/build/index.js

# Windows path (either format works)
node build/index.js C:/projects/mcp-server/build/index.js
node build/index.js C:\\projects\\mcp-server\\build\\index.js

Response Timing

  • 第一次响应可能需要长达30秒的时间
  • 这是正常的,发生在:
    • 服务器初始化时
    • Claude处理查询时
    • 工具执行时
  • 后续响应通常更快
  • 在初始等待期间不要中断进程

常见错误消息

如果您看到:

  • Error: Cannot find module:检查您的build文件夹并确保TypeScript编译成功
  • Connection refused:确保服务器正在运行且路径正确
  • Tool execution failed:验证工具所需的环境变量已设置
  • ANTHROPIC_API_KEY is not set:检查您的.env文件和环境变量
  • TypeError:确保您使用了正确的工具参数类型

Next Steps