1. 项目概述:30分钟在AWS Bedrock上构建MCP服务器的真相

最近在开发者社区里,一个标题为“我在30分钟内于AWS Bedrock上构建了一个MCP服务器,这是确切的代码”的项目引起了我的注意。作为一个在云服务和AI集成领域摸爬滚打多年的从业者,我的第一反应是既兴奋又怀疑。兴奋的是,如果这是真的,那意味着将模型上下文协议(Model Context Protocol, MCP)与AWS Bedrock这样的托管服务结合的门槛被极大地降低了;怀疑的是,30分钟这个时间点,听起来更像是一个营销口号,而非一个可复现的工程实践。

那么,这个项目到底是怎么回事?它真的能如标题所言,让一个具备基本云服务和编程知识的开发者在半小时内,就搭建起一个能与Claude、ChatGPT等AI助手通过MCP协议交互的服务器吗?经过我实际的代码审查、环境搭建和测试,我可以负责任地告诉你: 核心是可行的,但“30分钟”是一个理想化的、排除了所有前期准备和潜在坑点的“纯净”编码时间 。这个项目的真正价值,在于它提供了一个极其精简、直指核心的范例,展示了如何利用AWS Bedrock的SDK和MCP协议的基本框架,快速搭建一个功能性的桥梁。

简单来说,MCP(Model Context Protocol)是一个新兴的开放协议,旨在标准化AI助手(如Claude Desktop, Cursor)与外部工具、数据源和服务之间的通信。你可以把它想象成AI助手的“插件系统”或“驱动程序”标准。而AWS Bedrock是一项完全托管的服务,它让你能够通过统一的API访问来自AI21 Labs、Anthropic、Cohere、Meta、Stability AI和Amazon自身的一系列高性能基础模型。

这个项目的目标,就是构建一个MCP服务器,它作为中间件,接收来自AI助手(通过MCP客户端)的请求,将其转换为对AWS Bedrock API的调用,然后将模型的响应打包回MCP格式,返回给AI助手。这样一来,用户就可以在熟悉的AI助手界面中,直接、安全地调用托管在AWS上的强大模型。接下来,我将为你彻底拆解这个项目的每一个环节,从设计思路到每一行代码的意图,再到你实际部署时必然会遇到的“坑”和解决方案,让你不仅能复现,更能理解其背后的逻辑,甚至进行定制化扩展。

2. 核心架构与设计思路拆解

在动手写代码之前,理解我们到底要构建什么以及为什么这样设计,是节省大量调试时间的关键。这个30分钟项目的架构,本质上是一个轻量级的、基于HTTP的MCP服务器,它充当了MCP客户端(如Claude Desktop)和AWS Bedrock服务之间的翻译官和信使。

2.1 为什么选择这样的技术栈?

项目示例代码通常基于Node.js(或Python),这是一个合理的选择。原因有三点:首先, AWS SDK for JavaScript (v3) Python (boto3) 对Bedrock的支持非常成熟且官方,提供了最稳定、功能最全的API接口。其次,构建一个简单的HTTP服务器,在Node.js中使用Express.js框架或在Python中使用FastAPI框架,都是极其快速和直观的,符合“30分钟”的快速原型精神。最后,MCP协议本身有相对清晰的规范,实现几个核心的端点(如 /tools/list , /tools/call )和数据结构,对于有经验的开发者来说并不复杂。

整个数据流可以这样理解:

  1. 用户 在Claude Desktop中提出一个请求,例如“用Bedrock上的Claude 3 Sonnet总结这篇文档”。
  2. Claude Desktop (MCP客户端) 识别出这个请求需要调用一个“Bedrock总结工具”。它按照MCP协议,向一个预先配置好的服务器地址(即我们将要构建的服务器)发送一个HTTP POST请求到 /tools/call 端点,请求体中包含了工具名称和参数(如文档内容)。
  3. 我们的MCP服务器 接收到请求。它解析出要调用的模型(例如 anthropic.claude-3-sonnet-20240229-v1:0 )和输入的提示词。
  4. 服务器使用 AWS SDK ,携带你的AWS认证信息(通过环境变量管理),向Bedrock的 InvokeModel API发起请求。
  5. AWS Bedrock 服务处理请求,调用指定的模型,生成响应文本。
  6. Bedrock 将响应返回给我们的MCP服务器。
  7. 我们的MCP服务器 将Bedrock的响应体,重新包装成MCP协议规定的JSON格式(主要包含一个 content 字段),然后发回给Claude Desktop。
  8. Claude Desktop 收到响应,将其中的 content 展示给用户。

这个流程的核心在于 协议转换 安全的凭证管理 。我们的服务器代码主要就是处理这两件事。

2.2 项目结构预览

一个典型的、结构清晰的项目目录会是这样(以Node.js为例):

bedrock-mcp-server/
├── index.js                 # 主服务器文件,包含HTTP服务器和路由定义
├── bedrock-client.js        # 封装AWS Bedrock调用的专用模块
├── mcp-protocol.js          # 定义MCP协议相关的常量和辅助函数
├── package.json             # 项目依赖定义(express, @aws-sdk/client-bedrock-runtime)
├── .env.example             # 环境变量示例文件
└── README.md                # 部署和配置说明

这种分离关注点的设计,虽然对于“30分钟”的极简demo来说可能被压缩到一个文件里,但对于理解和后续维护至关重要。 bedrock-client.js 负责所有与AWS的交互,隔离了云服务商的细节; mcp-protocol.js 则确保我们发送和接收的数据符合MCP规范。

3. 环境准备与核心依赖解析

“30分钟”的起点,是从一个干净的开发环境开始的。我们假设你已经有了一个可用的AWS账户,并且本地安装了Node.js和npm(或Python和pip)。接下来,我们需要搞定三件关键事:AWS权限、本地依赖和MCP客户端配置。

3.1 AWS IAM配置:安全的第一步

这是整个项目中最容易出错,但也最重要的一环。你的代码需要通过AWS SDK进行认证,才能调用Bedrock。绝对不要将Access Key和Secret Key硬编码在代码里!标准的做法是使用AWS IAM(身份和访问管理)。

  1. 创建IAM用户 :登录AWS控制台,进入IAM服务。创建一个新的用户,例如命名为 mcp-bedrock-user 。在创建过程中, 选择“编程式访问” ,这将生成访问密钥ID和私有访问密钥。

  2. 附加权限策略 :这个用户需要调用Bedrock的权限。最简单(但权限范围较广)的方法是直接附加AWS托管策略 AmazonBedrockFullAccess 对于生产环境,这是一个糟糕的做法。 你应该遵循最小权限原则,创建一个自定义策略。一个最小化的自定义策略JSON可能如下所示:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "bedrock:InvokeModel",
                "Resource": "*" // 更佳实践是限定到特定模型ARN
            },
            {
                "Effect": "Allow",
                "Action": "bedrock:ListFoundationModels",
                "Resource": "*"
            }
        ]
    }
    

    将这个策略附加到刚才创建的IAM用户上。

  3. 获取并安全保存凭证 :创建用户后,AWS会提供一次性的 AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY 。请立即下载 .csv 文件并妥善保存。之后你将无法再查看完整的秘密访问密钥。

3.2 本地项目初始化与依赖安装

打开你的终端,开始初始化项目。

# 创建一个新目录并进入
mkdir bedrock-mcp-server && cd bedrock-mcp-server
# 初始化Node.js项目(如果用Python,则是创建虚拟环境和requirements.txt)
npm init -y

接下来安装核心依赖。我们需要两个东西:一个Web框架来构建HTTP服务器,以及官方的AWS SDK。

npm install express @aws-sdk/client-bedrock-runtime
  • express :轻量且高效的Node.js Web框架,用于快速搭建我们的MCP服务器端点。
  • @aws-sdk/client-bedrock-runtime :这是AWS SDK v3中专门用于调用Bedrock推理API的客户端包。SDK v3采用了模块化设计,比v2更轻量。

对于Python版本,你会使用:

pip install fastapi uvicorn boto3

3.3 配置环境变量与MCP客户端

在项目根目录创建一个名为 .env 的文件(注意,这个文件应该被添加到 .gitignore 中,避免密钥泄露)。内容如下:

# .env
AWS_ACCESS_KEY_ID=你的ACCESS_KEY
AWS_SECRET_ACCESS_KEY=你的SECRET_ACCESS_KEY
AWS_REGION=us-east-1 # 根据你Bedrock模型可用的区域填写,例如 us-west-2, ap-northeast-1
MCP_SERVER_PORT=3000 # 你的服务器将监听的端口

在代码中,我们将使用 dotenv 包(需要额外安装 npm install dotenv )来读取这些变量。

实操心得:区域(Region)的选择至关重要。 不是所有模型在所有区域都可用。例如,Anthropic的Claude 3系列在撰写本文时可能在 us-east-1 us-west-2 提供。你需要在AWS控制台的Bedrock页面查看模型可用性,并确保你请求的模型ID(如 anthropic.claude-3-sonnet-20240229-v1:0 )在你设置的 AWS_REGION 中是可用的。否则,你会收到 ResourceNotFoundException 错误。

最后,你需要配置你的MCP客户端(例如Claude Desktop)。通常,这需要在客户端的配置文件中添加一个服务器条目。对于Claude Desktop,配置文件位于 ~/Library/Application Support/Claude/claude_desktop_config.json (Mac)或类似位置。你需要添加如下配置:

{
  "mcpServers": {
    "bedrock-server": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/YOUR/bedrock-mcp-server/index.js"],
      "env": {
        "AWS_ACCESS_KEY_ID": "...",
        "AWS_SECRET_ACCESS_KEY": "...",
        "AWS_REGION": "..."
      }
    }
  }
}

注意 :更安全的做法不是在配置文件中写死密钥,而是让我们的服务器进程自己从 .env 或系统环境变量中读取。上述 env 部分在某些客户端配置中可用于传递变量,但最佳实践是确保你的服务器启动脚本能独立处理认证。

4. 核心代码实现与逐行解读

现在,让我们进入核心环节,看看这“30分钟”写出的代码到底长什么样,以及每一部分都在做什么。我将以一个Node.js + Express的版本为例进行拆解。

4.1 主服务器文件 (index.js):HTTP路由与MCP协议适配

这是服务器的入口和大脑,负责定义HTTP路由,并按照MCP协议规范处理请求和响应。

// index.js
require(‘dotenv’).config(); // 加载 .env 文件中的环境变量
const express = require(‘express’);
const { handleToolList, handleToolCall } = require(‘./mcp-handler’); // 假设我们将业务逻辑分离

const app = express();
const PORT = process.env.MCP_SERVER_PORT || 3000;

// 关键中间件:解析JSON格式的请求体
app.use(express.json());

// MCP协议核心端点1:列出服务器提供的所有工具
app.get(‘/tools/list’, async (req, res) => {
  try {
    const tools = await handleToolList();
    res.json(tools);
  } catch (error) {
    console.error(‘Error listing tools:’, error);
    res.status(500).json({ error: error.message });
  }
});

// MCP协议核心端点2:调用一个具体的工具
app.post(‘/tools/call’, async (req, res) => {
  const { name, arguments: toolArguments } = req.body;
  if (!name) {
    return res.status(400).json({ error: ‘Tool name is required’ });
  }

  try {
    const result = await handleToolCall(name, toolArguments);
    res.json(result);
  } catch (error) {
    console.error(`Error calling tool ${name}:`, error);
    // 根据错误类型返回更精确的状态码
    if (error.name === ‘ResourceNotFoundException’) {
      res.status(404).json({ error: `Model not found: ${error.message}` });
    } else if (error.name === ‘AccessDeniedException’) {
      res.status(403).json({ error: ‘Access denied to Bedrock’ });
    } else {
      res.status(500).json({ error: error.message });
    }
  }
});

// 可选的根端点,用于健康检查
app.get(‘/’, (req, res) => {
  res.send(‘Bedrock MCP Server is running.’);
});

app.listen(PORT, () => {
  console.log(`Bedrock MCP Server listening on port ${PORT}`);
  console.log(`MCP Tools endpoint: http://localhost:${PORT}/tools/list`);
});

代码解读与注意事项:

  1. 协议合规性 :MCP协议规范了特定的端点( /tools/list /tools/call )和HTTP方法(GET和POST)。我们必须严格遵守,否则客户端无法识别。
  2. 错误处理 :在 /tools/call 端点中,我们尝试捕获错误并返回有意义的HTTP状态码和消息。这对于调试至关重要。例如,将AWS Bedrock的 ResourceNotFoundException 映射为HTTP 404,明确告诉调用者模型找不到。
  3. 请求体解析 app.use(express.json()) 这行中间件必不可少,它允许我们轻松访问 req.body 中的JSON数据。

4.2 MCP协议处理器 (mcp-handler.js):业务逻辑核心

这个文件包含了具体的工具定义和调用逻辑。它隔离了协议层和具体的Bedrock服务层。

// mcp-handler.js
const { invokeBedrockModel } = require(‘./bedrock-client’);

// 定义服务器对外暴露的工具列表
async function handleToolList() {
  // 这里可以动态地从Bedrock的ListFoundationModels API获取模型列表
  // 但为了简单和快速启动,我们静态定义几个常用工具。
  return {
    tools: [
      {
        name: ‘invoke_claude_3_sonnet’,
        description: ‘调用AWS Bedrock上的Claude 3 Sonnet模型进行对话或文本生成。’,
        inputSchema: {
          type: ‘object’,
          properties: {
            prompt: {
              type: ‘string’,
              description: ‘发送给Claude模型的提示词(Prompt)’
            },
            max_tokens: {
              type: ‘number’,
              description: ‘生成回复的最大token数’,
              default: 1024
            }
          },
          required: [‘prompt’]
        }
      },
      {
        name: ‘invoke_claude_3_haiku’,
        description: ‘调用AWS Bedrock上更快、更经济的Claude 3 Haiku模型。’,
        inputSchema: { /* 类似结构 */ }
      }
      // 可以继续添加其他模型工具,如‘invoke_llama3_8b’等
    ]
  };
}

// 处理工具调用请求
async function handleToolCall(toolName, args) {
  // 根据工具名称,映射到对应的Bedrock模型ID和配置
  const toolConfigMap = {
    ‘invoke_claude_3_sonnet’: {
      modelId: ‘anthropic.claude-3-sonnet-20240229-v1:0’,
      provider: ‘anthropic’
    },
    ‘invoke_claude_3_haiku’: {
      modelId: ‘anthropic.claude-3-haiku-20240307-v1:0’,
      provider: ‘anthropic’
    }
  };

  const config = toolConfigMap[toolName];
  if (!config) {
    throw new Error(`Tool ‘${toolName}’ is not supported.`);
  }

  const { prompt, max_tokens = 1024 } = args;
  if (!prompt || typeof prompt !== ‘string’) {
    throw new Error(‘Invalid or missing “prompt” argument.’);
  }

  // 调用Bedrock客户端
  const bedrockResponse = await invokeBedrockModel(config.modelId, config.provider, prompt, max_tokens);

  // 将Bedrock的响应格式化为MCP协议要求的格式
  return {
    content: [
      {
        type: ‘text’,
        text: bedrockResponse // 这里假设invokeBedrockModel返回的是纯文本字符串
      }
    ]
  };
}

module.exports = { handleToolList, handleToolCall };

关键设计解析:

  1. 工具定义 handleToolList 返回的 tools 数组是MCP客户端(如Claude Desktop)用来展示可用工具列表的依据。 inputSchema 非常重要,它定义了客户端如何生成一个UI表单来收集用户输入。这里我们定义了一个 prompt 字符串参数和一个可选的 max_tokens 数字参数。
  2. 模型映射 :我们通过一个简单的映射对象 toolConfigMap ,将友好的工具名(如 invoke_claude_3_sonnet )映射到具体的Bedrock模型ID。这层抽象使得添加新模型变得非常容易。
  3. 响应格式化 :MCP协议要求 /tools/call 的返回值包含一个 content 数组。最常见的类型是 {type: ‘text’, text: ‘...’} 。我们必须确保 invokeBedrockModel 函数返回的字符串被正确包装在这个结构里。

4.3 Bedrock客户端封装 (bedrock-client.js):与AWS的桥梁

这是与AWS服务直接交互的部分,封装了SDK调用的细节。

// bedrock-client.js
const { BedrockRuntimeClient, InvokeModelCommand } = require(‘@aws-sdk/client-bedrock-runtime’);

// 初始化Bedrock客户端,SDK会自动从环境变量中读取 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION
const bedrockClient = new BedrockRuntimeClient();

async function invokeBedrockModel(modelId, provider, prompt, maxTokens) {
  // 根据模型提供商,构造不同的请求体格式
  let requestBody;
  if (provider === ‘anthropic’) {
    // Anthropic Claude 模型的消息格式
    requestBody = JSON.stringify({
      anthropic_version: ‘bedrock-2023-05-31’,
      max_tokens: maxTokens,
      messages: [
        {
          role: ‘user’,
          content: [{ type: ‘text’, text: prompt }]
        }
      ]
    });
  } else if (provider === ‘meta’) {
    // Meta Llama 3 模型的格式
    requestBody = JSON.stringify({
      prompt: prompt,
      max_gen_len: maxTokens,
      temperature: 0.5, // 可以添加更多参数
    });
  } else {
    throw new Error(`Unsupported model provider: ${provider}`);
  }

  const command = new InvokeModelCommand({
    modelId: modelId,
    contentType: ‘application/json’,
    accept: ‘application/json’,
    body: requestBody
  });

  try {
    const response = await bedrockClient.send(command);
    // 响应体是一个Uint8Array,需要解码并解析JSON
    const responseString = new TextDecoder().decode(response.body);
    const responseJson = JSON.parse(responseString);

    // 根据不同提供商的响应结构提取文本
    let generatedText;
    if (provider === ‘anthropic’) {
      // Claude响应格式
      generatedText = responseJson.content[0].text;
    } else if (provider === ‘meta’) {
      // Llama 3响应格式
      generatedText = responseJson.generation;
    } else {
      generatedText = responseString; // 回退
    }

    return generatedText;
  } catch (error) {
    // 将AWS SDK错误向上抛出,由上层处理
    console.error(‘AWS Bedrock invocation failed:’, error);
    throw error; // 重新抛出,让调用者(mcp-handler)处理
  }
}

module.exports = { invokeBedrockModel };

这是整个项目最需要小心的地方。 不同模型家族的API请求和响应格式差异巨大。

  1. 请求体构造 :Anthropic的Claude模型使用基于 messages 的对话格式,而Meta的Llama模型使用简单的 prompt 字段。你必须查阅对应模型的 Bedrock API文档 来构造正确的JSON。这里的代码只是一个示例,实际参数(如 temperature , top_p , stop_sequences )需要你根据需求添加。
  2. 响应体解析 :同样,Claude的响应在 responseJson.content[0].text 里,而Llama 3的在 responseJson.generation 里。解析错误会导致服务器返回空内容或崩溃。
  3. 错误处理 :这里我们只是记录并重新抛出错误。更健壮的做法可以包括重试逻辑(针对节流错误 ThrottlingException )和更详细的错误上下文记录。

5. 部署、测试与集成验证

代码写完了,但它真的能工作吗?让我们启动服务器并进行端到端的测试。

5.1 启动服务器与基础测试

首先,确保你的 .env 文件已正确配置。然后在终端运行:

node index.js

如果一切顺利,你会看到 Bedrock MCP Server listening on port 3000 的消息。

基础健康检查 :打开浏览器,访问 http://localhost:3000 ,应该能看到简单的欢迎信息。更重要的测试是调用MCP端点:

  1. 测试 /tools/list :使用 curl 或Postman向 GET http://localhost:3000/tools/list 发送请求。你应该收到一个JSON响应,其中包含你定义的 invoke_claude_3_sonnet 等工具列表。这验证了服务器基本路由和工具定义是正常的。

  2. 测试 /tools/call (模拟) :这是一个POST请求,需要构造JSON body。你可以先用一个简单的测试,不经过MCP客户端:

    curl -X POST http://localhost:3000/tools/call \
      -H “Content-Type: application/json” \
      -d ‘{“name”: “invoke_claude_3_sonnet”, “arguments”: {“prompt”: “Hello, world!”}}’
    

    如果AWS凭证和权限正确,且模型在指定区域可用,你应该会收到一个包含Claude生成文本的JSON响应。如果遇到AWS权限错误或模型未找到错误,请根据控制台输出信息回头检查IAM配置和区域设置。

5.2 与MCP客户端(Claude Desktop)集成

这是最终的验收测试。你需要配置Claude Desktop来使用你刚构建的服务器。

  1. 找到配置文件 :如前所述,找到你系统上的Claude Desktop配置文件。
  2. 编辑配置 :在 mcpServers 部分添加你的服务器配置。 关键点在于 command args 。如果你在本地开发,可以直接指向你的Node.js脚本。但更可靠的方式是写一个简单的启动脚本(如 start_server.sh start_server.bat ),在其中设置环境变量并启动Node。
    {
      “mcpServers”: {
        “my-bedrock-server”: {
          “command”: “/bin/bash”,
          “args”: [“-c”, “cd /ABSOLUTE/PATH/TO/YOUR/bedrock-mcp-server && AWS_REGION=us-east-1 node index.js”]
        }
      }
    }
    
    注意:以上是Unix/Linux/macOS的示例。Windows需要使用 cmd.exe 和不同的语法。
  3. 重启Claude Desktop :保存配置文件后,完全退出并重启Claude Desktop应用。
  4. 验证连接 :重启后,Claude Desktop会在后台启动你配置的服务器进程。你可以在Claude Desktop的设置或日志中查看MCP服务器连接状态。如果连接成功,当你在聊天框中输入时,Claude应该能“看到”你定义的工具(例如,输入“/”可能会提示出 invoke_claude_3_sonnet 工具)。
  5. 实际调用 :尝试使用工具。Claude Desktop会弹出一个表单让你输入 prompt ,提交后,请求会发送到你的服务器,服务器调用Bedrock,并将结果返回给Claude显示。

5.3 性能优化与生产就绪考量

“30分钟”的代码是一个原型。要用于更严肃的场景,你需要考虑以下几点:

  1. 认证与安全

    • 绝对不要 在代码或配置文件中硬编码密钥。
    • 在AWS EC2、ECS或Lambda上运行时,使用IAM角色(Instance Profile, Task Role)是比长期访问密钥更安全的方式。
    • 考虑为你的MCP服务器添加一层简单的API密钥认证,防止未授权的访问。
  2. 错误处理与健壮性

    • 增加更全面的输入验证。
    • 对Bedrock API调用实现指数退避重试机制,特别是处理 ThrottlingException
    • 添加请求超时控制,避免长时间挂起的请求阻塞服务器。
  3. 可扩展性

    • 将工具定义和模型配置外部化(如存入JSON配置文件或数据库),这样添加新模型无需修改代码。
    • 考虑支持流式响应(如果MCP客户端和Bedrock模型都支持),以提升长文本生成的用户体验。
  4. 日志与监控

    • 使用 winston pino 等日志库替代 console.log ,结构化日志便于查询和分析。
    • 记录每个工具调用的模型、输入token数(估算)、输出token数和耗时,这对于成本监控和性能分析至关重要。

6. 常见问题与故障排查实录

在实际搭建和运行过程中,你几乎一定会遇到下面这些问题。这里是我踩过坑后总结的排查清单。

6.1 权限与认证问题

问题:服务器启动或调用时出现 AccessDeniedException UnrecognizedClientException

  • 检查1:环境变量 。确保你的 .env 文件中的 AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY 与创建IAM用户时获得的完全一致,没有多余的空格或换行。可以在代码启动后立即 console.log(process.env.AWS_ACCESS_KEY_ID?.substring(0,5)) 来简单验证是否成功加载。
  • 检查2:IAM策略 。登录AWS控制台,找到对应的IAM用户,检查附加的策略是否包含 bedrock:InvokeModel bedrock:ListFoundationModels (如果你动态列出模型)的允许声明。确保没有显式的 Deny 语句覆盖。
  • 检查3:区域匹配 。确保 AWS_REGION 环境变量设置正确,并且你尝试调用的模型 在该区域已启用 。你需要登录AWS Bedrock控制台,在“模型访问”中请求并启用特定模型,它才能在API中使用。

6.2 模型调用失败

问题:调用时收到 ResourceNotFoundException ValidationException

  • 检查1:模型ID拼写 。模型ID必须完全正确,包括版本后缀。例如 anthropic.claude-3-sonnet-20240229-v1:0 。最好直接从AWS Bedrock控制台“Playground”或API文档中复制模型ID。
  • 检查2:请求体格式 。这是最常见的坑。 ValidationException 通常意味着你发送给Bedrock的JSON格式不符合该模型的要求。仔细对比你的 requestBody 和官方API示例。特别注意:
    • Claude模型需要 anthropic_version 字段。
    • 消息结构是 messages 数组,而不是简单的 prompt 字符串。
    • Llama模型可能需要 temperature 等参数。
    • 使用 console.log 在调用前打印出完整的 requestBody ,与文档进行逐字对比。

6.3 MCP客户端连接失败

问题:Claude Desktop无法连接服务器,或在日志中报错。

  • 检查1:服务器是否在运行 。首先确保你的 node index.js 进程正在运行,并且没有因为错误而退出。检查端口是否被占用。
  • 检查2:配置文件路径和命令 。Claude Desktop配置中的 command args 必须是绝对路径,并且有执行权限。在Unix系统上,你可以直接在终端中运行配置中的完整命令来测试它是否能独立启动服务器。
  • 检查3:环境变量传递 。如果服务器依赖环境变量,确保它们在Claude Desktop启动的上下文中可用。最稳妥的方式是在启动脚本(如shell脚本)内部设置环境变量。
  • 检查4:查看客户端日志 。Claude Desktop通常有详细的日志文件,位于其应用数据目录中。查看这些日志可以获得连接失败的具体原因,如“连接被拒绝”、“命令未找到”或“权限错误”。

6.4 响应解析错误

问题:服务器能调用Bedrock并返回数据,但Claude Desktop显示空白或错误,或者服务器自身在解析Bedrock响应时崩溃。

  • 检查1:响应格式解析 。在 bedrock-client.js invokeBedrockModel 函数中, console.log 打印出原始的 responseString 。确认其JSON结构是否与你代码中解析的路径一致(例如,Claude响应是 responseJson.content[0].text ,Llama 3是 responseJson.generation )。
  • 检查2:MCP响应格式 。确保你的 handleToolCall 函数最终返回的对象格式是 { content: [{ type: ‘text’, text: ‘...’ }] } content 是一个数组,这是MCP协议的要求。

6.5 性能与超时

问题:调用响应缓慢,或请求超时。

  • 调整超时设置 :AWS SDK和HTTP服务器(Express)都有默认的超时设置。对于生成长文本,可能需要增加超时时间。在Express中,你可以在路由层面或使用中间件设置更长的超时。
  • 监控Bedrock的延迟 :不同模型和不同区域的延迟不同。可以在代码中记录从发起请求到收到响应的耗时。如果某些模型持续很慢,考虑切换到其他区域或模型。
  • 实现异步处理 :对于非常耗时的请求,MCP协议支持异步操作。你可以先返回一个 { status: ‘pending’ } 的响应,然后在后台处理,并通过其他机制(如服务器发送事件)通知客户端结果。但这会显著增加复杂度,超出了“30分钟”原型的范畴。

构建这个MCP服务器的过程,与其说是一场与时间的赛跑,不如说是一次对云服务集成和协议抽象能力的精准练习。它剥离了所有不必要的装饰,直指问题的核心:如何安全、规范地将一个强大的托管AI服务,接入到日益流行的AI助手生态中。当你成功运行起这个服务器,并看到Claude通过它调用Bedrock模型生成回答时,你所获得的不仅仅是一个可用的工具,更是一种对现代AI应用栈分层和接口设计的深刻理解。这个简单的服务器可以作为一个坚实的起点,随着你的需求增长,逐步添加上认证、监控、流式响应、多模型路由等高级功能,最终演化成一个属于你自己的、强大的AI能力网关。

更多推荐