mcpField
B
Bedrock MCP Connector

Bedrock MCP Connector

github.com/juspayフレームワーク・SDK261 週間前に更新

Bedrock MCP Connector について

Bedrock MCP Connectorは、MCPサーバーと連携してAWS Bedrockモデルを利用するTypeScriptクライアントです。サーバーのツール検出、独自ハンドラー、イベントによるストリーミング、設定可能な会話保存に対応します。

主な機能

  1. 01AWS BedrockモデルとMCPサーバーの接続
  2. 02MCPツールの検出と利用
  3. 03独自ツールハンドラーの登録
  4. 04イベントによるレスポンス配信
  5. 05メモリまたはRedisへの会話保存
  6. 06対話型コマンドラインインターフェース

活用シーン

  1. 01BedrockモデルとMCPツールを使うアプリの構築
  2. 02モデルとのやり取りへの独自ツールの追加
  3. 03Redisでの複数ユーザー会話履歴の保存

MCP 対応

MCPサーバーに接続してツールを検出し、Bedrockモデルから利用できるようにします。独自のツールハンドラーも登録できます。

詳細

Bedrock MCP Connector

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?");

ツールの作成と登録

ツールは、LLM がアクションを実行したり、外部データにアクセスしたりできる強力な機能です。このセクションでは、ツールを効果的に作成して登録する方法を詳しく説明します。

ツール登録の基本

registerTool メソッドは、4 つのパラメーターを受け取ります。

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. 単一責任:各ツールは 1 つのことに適切に取り組むようにします
  2. 明確な命名:説明的で、アクションを示す名前を使用します(例:getCurrentTime、searchDatabase)
  3. 充実した説明:LLM がツールをいつ使うべきか理解できるよう、詳細な説明を記述します
  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?");
入力スキーマの設計

入力スキーマでは 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. システムプロンプトで明確な指示を提示する:ツールをいつ、どのように使うかを LLM に説明します
  2. 説明的なツール名を使用する:名前からツールの目的が明確に伝わるようにします
  3. パラメーターの説明を詳しく記述する:各パラメーターの役割を LLM が理解できるようにします
  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
);

これらのベストプラクティスに従うと、LLM が理解し、効果的に使用できるツールを作成できます。その結果、ユーザーにより良い応答を提供できます。

コマンドラインインターフェイス

このパッケージには、対話形式で利用できるコマンドラインインターフェイスが含まれています。

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> - エージェントにプロンプトを送信します
  • registerTool(name: string, handler: ToolHandler, description?: string, inputSchema?: Record<string, any>): void - カスタムツールを登録します
  • getTools(): Array<{ name: string; description?: string }> - 登録されているすべてのツールを取得します
  • getEmitter(): BedrockMCPClientEmitter - イベントエミッターを取得します
  • getAgent(): ConverseAgent - エージェントを取得します
  • 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 呼び出しにまたがってツールの結果を保持し、すべてのツールの結果が最終的な応答に含まれるようにします。これは、複雑な質問に答えるためにモデルが複数のツールを使用する必要がある場合に特に便利です。

必須パラメーターが不足している場合の対応

特定のパラメーターを必要とするツールを使用する際、モデルが不足している情報を尋ねる対話型のフローを実装できます。不足しているパラメーターに対応する方法は、主に次の 2 つです。

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

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。