Bedrock MCP Connector について
Bedrock MCP Connectorは、MCPサーバーと連携してAWS Bedrockモデルを利用するTypeScriptクライアントです。サーバーのツール検出、独自ハンドラー、イベントによるストリーミング、設定可能な会話保存に対応します。
主な機能
- 01AWS BedrockモデルとMCPサーバーの接続
- 02MCPツールの検出と利用
- 03独自ツールハンドラーの登録
- 04イベントによるレスポンス配信
- 05メモリまたはRedisへの会話保存
- 06対話型コマンドラインインターフェース
活用シーン
- 01BedrockモデルとMCPツールを使うアプリの構築
- 02モデルとのやり取りへの独自ツールの追加
- 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 つのことに適切に取り組むようにします
- 明確な命名:説明的で、アクションを示す名前を使用します(例:
getCurrentTime、searchDatabase) - 充実した説明:LLM がツールをいつ使うべきか理解できるよう、詳細な説明を記述します
- 徹底した入力検証:エラーを防ぐため、常に入力を検証します
- わかりやすいエラーメッセージ:問題が発生した場合は、明確なエラーメッセージを返します
- 一貫した戻り値の形式:常に同じ形式で結果を返します
- 適切なログ記録:デバッグに役立つよう、ログ記録を含めます
ツール登録の詳細な例
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"],
}
);
ツールを効果的に使用するためのヒント
- システムプロンプトで明確な指示を提示する:ツールをいつ、どのように使うかを LLM に説明します
- 説明的なツール名を使用する:名前からツールの目的が明確に伝わるようにします
- パラメーターの説明を詳しく記述する:各パラメーターの役割を LLM が理解できるようにします
- パラメーターの説明に例を含める:パラメーターに指定できる有効な値を示します
- 徹底した検証を実装する:処理を行う前にすべての入力を確認します
- 構造化データを返す:ツールの結果を一貫した形式にします
- エラーを適切に処理する:明確なエラーメッセージを提示します
- ツールを十分にテストする:さまざまな入力で動作することを確認します
高度なツール登録
より複雑なツールでは、保守性を高めるためにコードを構成できます。
// 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:session456bedrock-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);
会話の管理
このパッケージは会話履歴を自動的に保持するため、モデルと自然にやり取りを続けられます。これにより、モデルは以前のメッセージのコンテキストを記憶し、会話全体を通じて一貫性のある応答を提供できます。
会話履歴の仕組み
- モデルに送信したメッセージとモデルから受信したメッセージは、すべてメモリに保存されます
- 新しいリクエストをモデルに送信するたびに、会話履歴全体が含まれます
- これにより、モデルは以前のメッセージを参照し、コンテキストを維持できます
会話履歴の管理
以下のメソッドを使用して、会話履歴にアクセスし、管理できます。
// 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();
これにより、以前の会話のツール結果が新しい会話に表示されなくなります。
例:会話フロー全体
以下は、モデルが不足しているパラメーターを尋ねる、会話フロー全体の例です。
-
ユーザーが不完全なパラメーターで質問する:
User: "Can you calculate something for me? I want to multiply 42 by something." -
モデルが不足しているパラメーターを尋ねる:
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?" -
ユーザーが不足しているパラメーターを入力する:
User: "The second number is 7." -
モデルがパラメーターをすべて指定してツールを使用する:
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
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。