mcpField
B
Bedrock MCP Connector

Bedrock MCP Connector

github.com/juspay框架与 SDK261 周前更新

关于 Bedrock MCP Connector

Bedrock MCP Connector 是用于通过 MCP 服务调用 AWS Bedrock 模型的 TypeScript 客户端。它结合了服务端工具发现、自定义处理器、事件流式响应和可配置的对话存储。

主要功能

  1. 01连接 AWS Bedrock 模型与 MCP 服务
  2. 02发现并使用 MCP 工具
  3. 03注册自定义工具处理器
  4. 04通过事件流式传输响应
  5. 05在内存或 Redis 中存储对话
  6. 06使用交互式命令行界面

适用场景

  1. 01构建使用 Bedrock 模型和 MCP 工具的应用
  2. 02为模型交互添加自定义工具
  3. 03在 Redis 中持久化多用户对话历史

MCP 相关

客户端连接 MCP 服务、发现其工具,并将工具提供给 Bedrock 模型使用。它还支持注册自定义工具处理器。

详细介绍

Bedrock MCP 连接器

用于与 AWS Bedrock 和 MCP(Model Context Protocol)服务交互的 TypeScript 客户端。

功能

  • 无缝集成 AWS Bedrock 的 Converse API
  • 支持 Claude 和其他 Bedrock 模型
  • 连接 MCP 服务以发现和使用可用工具
  • 注册自定义工具处理程序
  • 基于事件的架构,支持流式响应
  • 支持 TypeScript,并提供完整的类型定义
  • 提供用于交互式使用的命令行界面
  • 采用模块化设计,便于集成到其他项目中

安装

npm install @juspay/bedrock-mcp-connector

使用方法

基本用法
import { BedrockMCPClient, LogLevel } from "@juspay/bedrock-mcp-connector";

// Create a client
const client = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  region: "us-east-1",
  systemPrompt: "You are a helpful assistant.",
  mcpServerUrl: "http://localhost:5713/sse", // Optional
});

// Set the log level (optional)
// LogLevel.INFO - Default, shows important information
// LogLevel.DEBUG - Shows detailed debugging information
// LogLevel.WARN - Shows only warnings and errors
// LogLevel.ERROR - Shows only errors
// LogLevel.NONE - Suppresses all logs
client.setLogLevel(LogLevel.INFO);

// Connect to MCP server (if URL is provided)
if (client.mcpServerUrl) {
  await client.connect();
}

// Send a prompt
const response = await client.sendPrompt("What is the capital of France?");
console.log("Response:", response);

// Disconnect when done
if (client.isConnectedToMCP()) {
  await client.disconnect();
}
使用 Redis 进行持久化存储

此软件包支持使用内存存储和 Redis 存储来保存对话历史记录:

import { BedrockMCPClient } from "@juspay/bedrock-mcp-connector";

// Using Redis storage for persistent conversations
const client = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  region: "us-east-1",
  sessionId: "user-session-123", // Unique session identifier
  userId: "user-456", // Optional user identifier
  storage: {
    type: "redis",
    config: {
      host: "localhost",
      port: 6379,
      password: "your-redis-password", // Optional
      db: 0, // Redis database number
      keyPrefix: "bedrock-mcp:", // Key prefix for Redis keys
      ttl: 86400, // TTL in seconds (24 hours)
      connectionOptions: {
        connectTimeout: 5000,
        lazyConnect: true
      }
    }
  }
});

// Conversations are now persistent across client restarts
const response = await client.sendPrompt("Remember this: my favorite color is blue");
console.log("Response:", response);

// Later, in a new client instance with the same sessionId...
const newClient = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  region: "us-east-1",
  sessionId: "user-session-123", // Same session ID
  storage: { type: "redis", config: { /* same config */ } }
});

const response2 = await newClient.sendPrompt("What's my favorite color?");
// The model will remember the previous conversation!
存储配置选项
内存存储(默认)
const client = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  region: "us-east-1",
  // No storage config = in-memory storage
  // OR explicitly specify:
  storage: { type: "memory" }
});
Redis 存储
const client = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  region: "us-east-1",
  storage: {
    type: "redis",
    config: {
      host: "localhost",        // Redis host (default: 'localhost')
      port: 6379,              // Redis port (default: 6379)
      password: "password",    // Redis password (optional)
      db: 0,                   // Redis database (default: 0)
      keyPrefix: "myapp:",     // Key prefix (default: 'bedrock-mcp:conversation:')
      ttl: 3600,              // TTL in seconds (default: 86400 - 24 hours)
      connectionOptions: {     // Additional Redis connection options
        connectTimeout: 5000,
        lazyConnect: true,
        retryDelayOnFailover: 100,
        maxRetriesPerRequest: 3
      }
    }
  }
});
使用事件侦听器
import { BedrockMCPClient, LogLevel } from "@juspay/bedrock-mcp-connector";

// Create a client
const client = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  region: "us-east-1",
});

// Set the log level (optional)
client.setLogLevel(LogLevel.INFO);

// Set up event listeners
const emitter = client.getEmitter();

emitter.on("message", (message) => {
  console.log(`Message: ${message}`);
});

emitter.on("error", (error) => {
  console.error(`Error: ${error.message}`);
});

emitter.on("tool:start", (toolName, input) => {
  console.log(`Tool started: ${toolName}`);
});

emitter.on("tool:end", (toolName, result) => {
  console.log(`Tool completed: ${toolName}`);
});

emitter.on("response:start", () => {
  console.log("Response started");
});

emitter.on("response:chunk", (chunk) => {
  console.log(`Response chunk: ${chunk.substring(0, 50)}...`);
});

emitter.on("response:end", (fullResponse) => {
  console.log("Response completed");
});

// Send a prompt
const response = await client.sendPrompt("What is the capital of France?");

创建和注册工具

工具是一项强大的功能,可让大模型执行操作并访问外部数据。本节将详细介绍如何有效地创建和注册工具。

工具注册基础

registerTool 方法接受四个参数:

client.registerTool(
  name, // String: Unique identifier for the tool
  handler, // Function: Async function that implements the tool
  description, // String: Human-readable description of what the tool does
  inputSchema // Object: JSON Schema defining the tool's parameters
);
工具设计最佳实践
  1. 职责单一:每个工具都应做好一件事
  2. 命名清晰:使用描述性、以操作为导向的名称(例如,getCurrentTime、searchDatabase)
  3. 描述全面:提供详细描述,帮助大模型了解何时使用该工具
  4. 充分验证输入:始终验证输入以防止出错
  5. 错误消息明确:发生问题时返回清晰的错误消息
  6. 返回格式一致:始终以相同格式返回结果
  7. 适当记录日志:添加日志以便调试
详细的工具注册示例
import { BedrockMCPClient, LogLevel } from "@juspay/bedrock-mcp-connector";

// Create a client
const client = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  region: "us-east-1",
});

// Set the log level (optional)
client.setLogLevel(LogLevel.INFO);

// Register a custom tool
client.registerTool(
  // Name: Use a clear, descriptive name
  "getCurrentTime",

  // Handler: Implement the tool's functionality
  async (name, input) => {
    // Input validation with default values
    const timezone = input.timezone || "UTC";

    try {
      // Core functionality
      const date = new Date().toLocaleString("en-US", { timeZone: timezone });

      // Return successful result
      return {
        content: [{ text: `The current time is ${date} in ${timezone}` }],
      };
    } catch (error) {
      // Error handling
      return {
        content: [{ text: `Error getting time: ${error.message}` }],
        isError: true,
      };
    }
  },

  // Description: Clearly explain what the tool does
  "Get the current time in the specified timezone. This tool returns the current date and time formatted according to US locale conventions.",

  // Input Schema: Define the parameters using JSON Schema
  {
    type: "object",
    properties: {
      timezone: {
        type: "string",
        description:
          "The timezone to get the time for (e.g., UTC, America/New_York, Europe/London)",
        examples: ["UTC", "America/New_York", "Europe/Paris", "Asia/Tokyo"],
      },
    },
    required: [], // Empty array means no parameters are required
  }
);

// Send a prompt that might use the tool
const response = await client.sendPrompt("What time is it now in Tokyo?");
输入 Schema 设计

输入 Schema 使用 JSON Schema 格式定义工具接受的参数:

{
  type: "object",
  properties: {
    // Define each parameter
    paramName: {
      type: "string" | "number" | "boolean" | "array" | "object",
      description: "Clear description of the parameter",
      examples: ["example1", "example2"], // Optional but helpful
      enum: ["option1", "option2"],       // For parameters with fixed options
      minimum: 1,                         // For number validation
      maximum: 100,                       // For number validation
      pattern: "^[a-z]+$",                // For string validation with regex
      // Additional JSON Schema properties as needed
    },
    // More parameters...
  },
  required: ["paramName1", "paramName2"],  // List required parameters
  additionalProperties: false              // Prevent extra parameters (optional)
}
工具中的错误处理

正确处理错误至关重要:

client.registerTool(
  "divideNumbers",
  async (name, input) => {
    // Parameter validation
    if (typeof input.dividend !== "number") {
      return {
        content: [{ text: "Error: dividend must be a number" }],
        isError: true,
      };
    }

    if (typeof input.divisor !== "number") {
      return {
        content: [{ text: "Error: divisor must be a number" }],
        isError: true,
      };
    }

    // Business logic validation
    if (input.divisor === 0) {
      return {
        content: [{ text: "Error: Cannot divide by zero" }],
        isError: true,
      };
    }

    try {
      // Perform the operation
      const result = input.dividend / input.divisor;

      return {
        content: [
          {
            text: `${input.dividend} divided by ${input.divisor} equals ${result}`,
          },
        ],
      };
    } catch (error) {
      return {
        content: [{ text: `Calculation error: ${error.message}` }],
        isError: true,
      };
    }
  },
  "Divide two numbers",
  {
    type: "object",
    properties: {
      dividend: {
        type: "number",
        description: "The number to be divided",
      },
      divisor: {
        type: "number",
        description: "The number to divide by (cannot be zero)",
      },
    },
    required: ["dividend", "divisor"],
  }
);
不同类型工具的示例
1. 数据检索工具
client.registerTool(
  "getWeatherForecast",
  async (name, input) => {
    const { city, days = 3 } = input;

    if (!city) {
      return {
        content: [{ text: "Error: city parameter is required" }],
        isError: true,
      };
    }

    try {
      // In a real implementation, this would call a weather API
      const forecast = await weatherService.getForecast(city, days);

      return {
        content: [
          {
            text: `Weather forecast for ${city} for the next ${days} days:\n\n${forecast}`,
          },
        ],
      };
    } catch (error) {
      return {
        content: [{ text: `Error getting weather forecast: ${error.message}` }],
        isError: true,
      };
    }
  },
  "Get weather forecast for a city",
  {
    type: "object",
    properties: {
      city: {
        type: "string",
        description: "The city to get the weather forecast for",
      },
      days: {
        type: "number",
        description: "Number of days to forecast (default: 3)",
        minimum: 1,
        maximum: 10,
      },
    },
    required: ["city"],
  }
);
2. 计算工具
client.registerTool(
  "calculateStatistics",
  async (name, input) => {
    const { numbers } = input;

    if (!Array.isArray(numbers) || numbers.length === 0) {
      return {
        content: [
          { text: "Error: numbers must be a non-empty array of numbers" },
        ],
        isError: true,
      };
    }

    if (!numbers.every((n) => typeof n === "number")) {
      return {
        content: [{ text: "Error: all elements in numbers must be numbers" }],
        isError: true,
      };
    }

    try {
      const sum = numbers.reduce((a, b) => a + b, 0);
      const mean = sum / numbers.length;
      const sortedNumbers = [...numbers].sort((a, b) => a - b);
      const median =
        sortedNumbers.length % 2 === 0
          ? (sortedNumbers[sortedNumbers.length / 2 - 1] +
              sortedNumbers[sortedNumbers.length / 2]) /
            2
          : sortedNumbers[Math.floor(sortedNumbers.length / 2)];

      return {
        content: [
          {
            text: `Statistics for [${numbers.join(
              ", "
            )}]:\n- Sum: ${sum}\n- Mean: ${mean}\n- Median: ${median}\n- Min: ${Math.min(
              ...numbers
            )}\n- Max: ${Math.max(...numbers)}`,
          },
        ],
      };
    } catch (error) {
      return {
        content: [{ text: `Error calculating statistics: ${error.message}` }],
        isError: true,
      };
    }
  },
  "Calculate basic statistics for an array of numbers",
  {
    type: "object",
    properties: {
      numbers: {
        type: "array",
        items: {
          type: "number",
        },
        description: "Array of numbers to calculate statistics for",
      },
    },
    required: ["numbers"],
  }
);
3. 外部 API 工具
client.registerTool(
  "searchWikipedia",
  async (name, input) => {
    const { query, limit = 3 } = input;

    if (!query || typeof query !== "string") {
      return {
        content: [
          { text: "Error: query parameter is required and must be a string" },
        ],
        isError: true,
      };
    }

    try {
      // In a real implementation, this would call the Wikipedia API
      const searchUrl = `https://en.wikipedia.org/w/api.php?action=opensearch&search=${encodeURIComponent(
        query
      )}&limit=${limit}&namespace=0&format=json`;
      const response = await fetch(searchUrl);
      const [searchTerm, titles, descriptions, urls] = await response.json();

      let resultText = `Wikipedia search results for "${query}":\n\n`;

      for (let i = 0; i < titles.length; i++) {
        resultText += `${i + 1}. ${titles[i]}\n`;
        resultText += `   ${descriptions[i]}\n`;
        resultText += `   ${urls[i]}\n\n`;
      }

      return {
        content: [{ text: resultText }],
      };
    } catch (error) {
      return {
        content: [{ text: `Error searching Wikipedia: ${error.message}` }],
        isError: true,
      };
    }
  },
  "Search Wikipedia for information on a topic",
  {
    type: "object",
    properties: {
      query: {
        type: "string",
        description: "The search query",
      },
      limit: {
        type: "number",
        description: "Maximum number of results to return (default: 3)",
        minimum: 1,
        maximum: 10,
      },
    },
    required: ["query"],
  }
);
有效使用工具的技巧
  1. 在系统提示词中提供明确指示:指导大模型何时以及如何使用工具
  2. 使用描述性工具名称:名称应清楚表明工具的用途
  3. 提供详细的参数描述:帮助大模型了解每个参数的作用
  4. 在参数描述中添加示例:展示参数的有效值
  5. 实施充分的验证:处理前检查所有输入
  6. 返回结构化数据:以一致的格式整理工具结果
  7. 妥善处理错误:提供清晰的错误消息
  8. 充分测试工具:确保工具能处理各种输入
高级工具注册

对于更复杂的工具,可以采用结构化代码,让其更易于维护:

// Define tool handlers in separate files
import { searchDatabase } from "./tools/database.js";
import { processImage } from "./tools/image.js";
import { translateText } from "./tools/translation.js";

// Import tool schemas
import {
  searchDatabaseSchema,
  processImageSchema,
  translateTextSchema,
} from "./schemas/toolSchemas.js";

// Register tools
client.registerTool(
  "searchDatabase",
  searchDatabase,
  "Search the database for records matching the query",
  searchDatabaseSchema
);

client.registerTool(
  "processImage",
  processImage,
  "Process and analyze an image",
  processImageSchema
);

client.registerTool(
  "translateText",
  translateText,
  "Translate text between languages",
  translateTextSchema
);

遵循这些最佳实践,你就能创建出易于大模型理解和有效使用的工具,从而为用户提供更好的响应。

命令行界面

此软件包包含一个用于交互式使用的命令行界面:

npx @juspay/bedrock-mcp-connector

或者全局安装:

npm install -g @juspay/bedrock-mcp-connector
@juspay/bedrock-mcp-connector
CLI 选项
Options:
  -m, --model <id>           AWS Bedrock model ID (default: anthropic.claude-3-sonnet-20240229-v1:0)
  -r, --region <region>      AWS region (default: us-east-1)
  -s, --system-prompt <text> System prompt for the model
  -u, --mcp-url <url>        MCP server URL
  -n, --name <name>          Client name
  -v, --version <version>    Client version
  -h, --help                 Show this help message

会话管理和多用户支持

此软件包支持完善的会话管理,适用于多用户应用:

会话隔离
// User 1's conversation
const user1Client = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  region: "us-east-1",
  sessionId: "session-user1-chat1",
  userId: "user1",
  storage: { type: "redis", config: { /* redis config */ } }
});

// User 2's conversation (completely isolated)
const user2Client = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  region: "us-east-1",
  sessionId: "session-user2-chat1",
  userId: "user2",
  storage: { type: "redis", config: { /* redis config */ } }
});
存储健康监控
// Check if storage is healthy
const isHealthy = await client.isStorageHealthy();
if (!isHealthy) {
  console.log("Storage connection issues detected");
}

// Get storage information
const storageInfo = client.getStorageInfo();
console.log("Storage type:", storageInfo.type); // 'memory' or 'redis'
Redis 键管理

使用 Redis 存储时,键的结构如下:

  • 模式:{keyPrefix}{userId}:{sessionId} 或 {keyPrefix}{sessionId}
  • 默认前缀:bedrock-mcp:conversation:
  • 键示例:
    • bedrock-mcp:conversation:user123:session456
    • bedrock-mcp:conversation:anonymous-session789
存储迁移

你可以通过复制对话历史记录,在不同存储类型之间迁移:

// Get history from in-memory client
const memoryClient = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  storage: { type: "memory" }
});

const history = await memoryClient.getConversationHistory();

// Create Redis client and restore history
const redisClient = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  storage: { type: "redis", config: { /* config */ } }
});

// Note: Direct history restoration requires custom implementation
// The storage layer handles this automatically for same-session clients

API 参考

BedrockMCPClient
构造函数
new BedrockMCPClient(config: {
  modelId: string;
  region?: string;
  systemPrompt?: string;
  mcpServerUrl?: string;
  clientName?: string;
  clientVersion?: string;
  maxTokens?: number;
  temperature?: number;
  responseOutputTags?: [string, string];
  storage?: StorageConfig;
  sessionId?: string;
  userId?: string;
})
方法
  • connect(): Promise<void> - 连接到 MCP 服务
  • disconnect(): Promise<void> - 断开与 MCP 服务的连接并关闭存储
  • isConnectedToMCP(): boolean - 检查客户端是否已连接到 MCP 服务
  • sendPrompt(prompt: string): Promise<string> - 向 Agent 发送提示词
  • registerTool(name: string, handler: ToolHandler, description?: string, inputSchema?: Record<string, any>): void - 注册自定义工具
  • getTools(): Array<{ name: string; description?: string }> - 获取所有已注册的工具
  • getEmitter(): BedrockMCPClientEmitter - 获取事件发送器
  • getAgent(): ConverseAgent - 获取 Agent
  • getConversationHistory(): Promise<Message[]> - 获取对话历史记录
  • clearConversationHistory(): Promise<void> - 清除对话历史记录
  • setLogLevel(level: LogLevel): void - 设置客户端及其组件的日志级别
  • isStorageHealthy(): Promise<boolean> - 检查存储是否正常
  • getStorageInfo(): { type: string; isHealthy?: boolean } - 获取存储类型信息
存储类型
StorageConfig
type StorageConfig =
  | { type: 'memory' }
  | { type: 'redis'; config: RedisStorageConfig };
RedisStorageConfig
interface RedisStorageConfig {
  host?: string;                    // Redis host (default: 'localhost')
  port?: number;                    // Redis port (default: 6379)
  password?: string;                // Redis password
  db?: number;                      // Redis database number (default: 0)
  keyPrefix?: string;               // Key prefix (default: 'bedrock-mcp:conversation:')
  ttl?: number;                     // TTL in seconds (default: 86400)
  connectionOptions?: {             // Additional Redis connection options
    connectTimeout?: number;
    lazyConnect?: boolean;
    retryDelayOnFailover?: number;
    maxRetriesPerRequest?: number;
    [key: string]: any;
  };
}
SessionIdentifier
interface SessionIdentifier {
  sessionId: string;                // Unique session ID
  userId?: string;                  // Optional user ID for multi-user scenarios
}
事件
  • message - 记录消息时触发
  • error - 发生错误时触发
  • tool:start - 工具开始执行时触发
  • tool:end - 工具执行完毕时触发
  • response:start - 响应开始时触发
  • response:chunk - 收到响应片段时触发
  • response:end - 响应结束时触发
  • connected - 连接到 MCP 服务时触发
  • disconnected - 断开与 MCP 服务的连接时触发

日志系统

该软件包包含一套全面的日志系统,可用于控制日志和调试信息的详细程度。

日志级别

提供以下日志级别(从最详细到最简略):

  • LogLevel.DEBUG (0) - 详细的调试信息
  • LogLevel.INFO (1) - 常规信息消息(默认)
  • LogLevel.WARN (2) - 警告消息
  • LogLevel.ERROR (3) - 错误消息
  • LogLevel.NONE (4) - 不记录日志
设置日志级别

你可以为客户端及其所有组件设置日志级别:

import { BedrockMCPClient, LogLevel } from "@juspay/bedrock-mcp-connector";

// Create a client
const client = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  region: "us-east-1",
});

// Set the log level
client.setLogLevel(LogLevel.INFO); // Default level
// or
client.setLogLevel(LogLevel.DEBUG); // For detailed debugging
各日志级别记录的内容
  • DEBUG:所有消息,包括详细的 API 响应、工具执行详情和内部状态变化
  • INFO:常规运行信息、工具请求、连接状态和重要事件
  • WARN:不会阻止运行但可能需要关注的潜在问题
  • ERROR:影响运行但不一定导致应用崩溃的错误
  • NONE:不输出任何日志(未处理的异常除外)
使用场景
开发和调试(LogLevel.DEBUG)

在以下情况下使用 DEBUG 级别:

  • 开发新功能或工具
  • 排查 API 响应问题
  • 调试工具执行问题
  • 了解数据在系统中的流转过程
// Enable detailed debugging
client.setLogLevel(LogLevel.DEBUG);

// Now you'll see detailed information about:
// - Full API responses from Bedrock
// - Tool input and output details
// - Message content and processing
生产环境使用(LogLevel.INFO 或 LogLevel.WARN)

在生产环境中:

  • 使用 INFO 跟踪常规运行情况
  • 使用 WARN 仅查看潜在问题
// For normal operation with important information
client.setLogLevel(LogLevel.INFO);

// Or for minimal logging (only warnings and errors)
client.setLogLevel(LogLevel.WARN);
审计和监控(LogLevel.INFO)

需要跟踪工具使用情况和交互时:

client.setLogLevel(LogLevel.INFO);

// This will log:
// - When tools are requested and executed
// - Connection events
// - Response start/end events
静默运行(LogLevel.NONE)

需要禁止所有日志时:

client.setLogLevel(LogLevel.NONE);

对话管理

该软件包会自动维护对话历史记录,让你能够与模型进行自然的来回交流。这样,模型便可记住先前消息中的上下文,并在整个对话过程中给出连贯的响应。

对话历史记录的工作方式
  1. 发送给模型和从模型接收的每条消息都会存储在内存中
  2. 每次向模型发送新请求时,都会附带完整的对话历史记录
  3. 这样模型便可参考先前的消息并保持上下文
管理对话历史记录

你可以使用以下方法访问和管理对话历史记录:

// Get the current conversation history
const history = client.getConversationHistory();
console.log("Current conversation:", history);

// Clear the conversation history to start a new conversation
client.clearConversationHistory();
console.log("Started a new conversation");
示例:多轮对话
import { BedrockMCPClient } from "@juspay/bedrock-mcp-connector";

const client = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  region: "us-east-1",
});

// First message
let response = await client.sendPrompt("What are the three primary colors?");
console.log("Response 1:", response);

// Follow-up question (model remembers the context)
response = await client.sendPrompt(
  "And what colors do you get when you mix them?"
);
console.log("Response 2:", response);

// Start a new conversation
client.clearConversationHistory();

// This is now a completely new conversation with no memory of the previous exchange
response = await client.sendPrompt("What's the tallest mountain in the world?");
console.log("New conversation response:", response);
跨多个 API 调用保留工具结果

该软件包会在多个 API 调用之间保留工具结果,确保最终响应中包含所有工具结果。当模型需要使用多个工具来回答复杂问题时,这尤其有用。

处理缺失的必需参数

使用需要特定参数的工具时,你可以实现交互流程,让模型询问缺失的信息。处理缺失参数有两种主要方法:

1. 使用专用系统提示词

你可以通过专用系统提示词引导模型的行为,指示它如何处理缺失参数:

const client = new BedrockMCPClient({
  modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
  region: "us-east-1",
  systemPrompt: `You are a helpful assistant with access to external tools.
                  - Use available tools **only when necessary** to provide accurate or up-to-date information.
                  - If a question can be answered based on your knowledge, respond directly **without using tools**.
                  - If a tool is required:
                      1. **Check if all necessary parameters are available.** If they are, use the tool directly.
                      2. **If any parameters are missing, do not proceed.** Instead, ask the user for the required information, explaining why it is needed.
                      3. **Wait for the user's response before using the tool.**
                  - If the user asks multiple questions, **handle them one by one**.
                  - If some questions require tools and others don't, **answer what you can immediately**, then use tools as needed.
                  - After using a tool, continue answering any remaining questions.
                  `,
});

此系统提示词指示模型:

  • 不要尝试使用参数不完整的工具
  • 清楚说明缺少哪些参数以及需要这些参数的原因
  • 向用户询问缺失的信息
  • 仅在所有必需参数都可用后使用工具
2. 在工具中实现参数验证

在工具处理程序中实现全面的参数验证,确保它们能够妥善处理缺失参数:

client.registerTool(
  "calculator",
  async (name, input) => {
    const { operation, a, b } = input;

    // Validate required parameters
    if (!operation) {
      return {
        content: [
          { text: `Error: Operation parameter is required for calculator` },
        ],
        isError: true,
      };
    }

    if (a === undefined || a === null) {
      return {
        content: [
          { text: `Error: First operand (a) is required for calculator` },
        ],
        isError: true,
      };
    }

    if (b === undefined || b === null) {
      return {
        content: [
          { text: `Error: Second operand (b) is required for calculator` },
        ],
        isError: true,
      };
    }

    // Tool implementation...
    let result;
    switch (operation) {
      case "add":
        result = a + b;
        break;
      case "subtract":
        result = a - b;
        break;
      case "multiply":
        result = a * b;
        break;
      case "divide":
        if (b === 0) throw new Error("Division by zero");
        result = a / b;
        break;
      default:
        throw new Error(`Unknown operation: ${operation}`);
    }

    return {
      content: [{ text: `The result of ${a} ${operation} ${b} is ${result}` }],
    };
  },
  "Perform basic arithmetic operations",
  {
    type: "object",
    properties: {
      operation: {
        type: "string",
        description:
          "The operation to perform (add, subtract, multiply, divide)",
        enum: ["add", "subtract", "multiply", "divide"],
      },
      a: { type: "number", description: "The first operand" },
      b: { type: "number", description: "The second operand" },
    },
    required: ["operation", "a", "b"],
  }
);
3. 交互式对话流程

实现一个交互式对话流程,处理用户与模型之间的来回交流:

import { BedrockMCPClient } from "@juspay/bedrock-mcp-connector";
import * as readline from "readline";

// Create a readline interface for user input
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

// Function to get user input
const getUserInput = (question) =>
  new Promise((resolve) => {
    rl.question(question, (answer) => resolve(answer));
  });

async function runInteractiveSession() {
  const client = new BedrockMCPClient({
    modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
    region: "us-east-1",
    systemPrompt: `You are a helpful assistant with access to external tools.
                  - Use available tools **only when necessary** to provide accurate or up-to-date information.
                  - If a question can be answered based on your knowledge, respond directly **without using tools**.
                  - If a tool is required:
                      1. **Check if all necessary parameters are available.** If they are, use the tool directly.
                      2. **If any parameters are missing, do not proceed.** Instead, ask the user for the required information, explaining why it is needed.
                      3. **Wait for the user's response before using the tool.**
                  - If the user asks multiple questions, **handle them one by one**.
                  - If some questions require tools and others don't, **answer what you can immediately**, then use tools as needed.
                  - After using a tool, continue answering any remaining questions.
                  `,
  });

  // Register tools (calculator, weather forecast, etc.)
  // ...

  // Start the conversation
  console.log("Ask me anything! (Type 'exit' to quit)");

  while (true) {
    // Get user input
    const userInput = await getUserInput("> ");

    if (userInput.toLowerCase() === "exit") break;

    // Send to the model
    const response = await client.sendPrompt(userInput);
    console.log("\nAssistant:", response);
  }

  rl.close();
}

runInteractiveSession().catch(console.error);
4. 管理对话之间的工具结果

实现多轮对话时,清除不同对话之间累积的工具结果非常重要,以防先前对话的结果影响新对话:

// After completing a conversation or when starting a new one
client.clearConversationHistory();
client.getAgent().clearAccumulatedToolResults();

这样可以确保先前对话中的工具结果不会出现在新对话中。

示例:完整的对话流程

以下是一个完整对话流程示例,其中模型会询问缺失的参数:

  1. 用户提出的问题缺少参数:

    User: "Can you calculate something for me? I want to multiply 42 by something."
    
  2. 模型回复并询问缺失的参数:

    Assistant: "I'd be happy to help you with that calculation. To use the calculator tool,
    I need two operands (numbers) and the operation. You've provided one number (42) and
    mentioned you want to multiply, but I'm missing the second operand.
    Could you please tell me what number you want to multiply 42 by?"
    
  3. 用户提供缺失的参数:

    User: "The second number is 7."
    
  4. 模型使用参数完整的工具:

    Assistant: "I've calculated that 42 multiplied by 7 equals 294."
    

这种模式支持自然对话,模型可以请求缺失的信息,然后使用这些信息完成工具执行。

创建自定义日志记录器

你可以为应用程序的其他部分创建自己的日志记录器:

import { createDefaultLogger, LogLevel } from "@juspay/bedrock-mcp-connector";

// Create a logger with a custom prefix
const logger = createDefaultLogger("MyComponent");

// Set the log level
logger.setLevel(LogLevel.INFO);

// Use the logger
logger.info("Application started");
logger.debug("This won't be shown unless log level is DEBUG");
logger.warn("Something might be wrong");
logger.error("An error occurred", errorObject);

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。