1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SREの一次調査、Bedrock Agentに任せた:AWS API MCP ServerでAWS APIを触らせる

1
Last updated at Posted at 2026-03-19

はじめに

 最近、AWS API MCP Server というソリューションが公開されていることを知りました。READMEを読むと、Bedrock AgentCore Runtimeにデプロイして、MCP Serverとして動作させることもできるみたいです。
 今回は検証として、AWS API MCP ServerをデプロイしてそれをBedrock Agentから呼べるようにして、CloudWatch Logs上で発生したErrorログの調査を全自動でさせてみようという記事となります。
 インシデント発生時には、手動での調査に時間がかかり、問題の特定と対応に遅延が生じることがあります。今回の構成では、CloudWatch Alarmから自動的にBedrock Agentが起動され、CloudWatch Logs、CloudTrail、EC2などのAWSリソースを自律的に調査します。これにより、将来的にインシデント対応時間を大幅に短縮できるようになるのが狙いです。

 ※この記事は、実際に動かしながら実装した実践記事です。ところどころ感覚で作っている部分があるので、あくまでご参考としてください。
 ※Lambdaのコードなどは主にKiroやGitHub Copilotが書いています。動作を保証するものではありませんので、自己責任でご参照ください。

システム構成図

 本システムの全体アーキテクチャは以下の通りです。

CloudWatch Logs (ERRORログ)
  ↓ Metric Filter
CloudWatch Alarm
  ↓ SNS Topic (trigger)
Lambda (incident-invoke-agent)
  ↓ invoke_agent API
Bedrock Agent (incident_investigation)
  ↓ Action Group
Lambda (mcp-gateway)
  ↓ InvokeAgentRuntime API
Bedrock AgentCore Runtime (AWS API MCP Server)
  ↓ ReadOnlyAccess
AWS API (EC2, CloudWatch Logs, CloudTrail, etc.)
  ↓
調査結果
  ↓ SNS Topic (notify)
メール通知

image.png

 各コンポーネントの役割を下表にまとめます。

# リソース名 説明
CloudWatch Logs + Metric Filter アプリケーションのERRORログを監視し、特定のキーワードにマッチした場合、CloudWatch Alarmをトリガーします。
CloudWatch Alarm + SNS アラームが発火し、SNS トピックを通じて downstream のLambdaを起動します。
Lambda (incident-invoke-agent) SNSメッセージを受け取り、Bedrock Agent Runtimeのinvoke_agent APIを呼び出してAgentを起動します。
Bedrock Agent LLMに基づいた自律的なエージェント。MCPプロトコルを通じてAWSサービスを呼び出し、調査を実行します。
Lambda (mcp-gateway) Bedrock AgentとAgentCore Runtimeの橋渡し。MCPプロトコルの初期化から Tool呼び出しまでを処理します。
Bedrock AgentCore Runtime AWS API MCP Serverをホスティングするマネージドサービス。
AWS API MCP Server AWS CLI コマンドをMCPツールとして提供し、IAMロールの権限範囲内でAWSリソースにアクセスします。
SNS Topic (notify) 調査結果を受け取り、登録されたメールアドレスに通知します。

登場するAWSサービス・概念

Amazon Bedrock Agents

 Bedrock Agentsは、AI Agentを作ることができるサービスです。通常のLLMは質問に対して自動生成テキストを返すだけですが、Agentはツール呼び出し(Tool Use)や、複数ステップの推論を行ったりできます。

 本システムでは以下の特徴を活用しています:

  • Action Group: Lambdaを通じて外部APIを呼び出すインターフェース
  • Session Attributes: ステートを保持し、マルチターン会話に対応

モデルは、us.anthropic.claude-sonnet-4-20250514-v1:0を利用しました。

Amazon Bedrock AgentCore Runtime

 AgentCore Runtimeは、MCPサーバーをホスティングするマネージドサービスです。今回はAWS API MCP Serverを実行し、LLMがAWSサービスのツールを利用できるようにします。

AWS API MCP Server

 AWS API MCP Serverは、AWSがオープンソースとして提供する、AWS CLIコマンドをMCPツールとして公開するサーバーです。

 主要ツール:

  • call_aws: AWS CLI commands を通じてAWSサービスにアクセスする
  • suggest_aws_commands: 現在の状況に応じて実行すべきコマンドを提案

 実行ロール(ReadOnlyAccess)の権限範囲内でのみ動作するため、セキュリティ面でも安心です。

デプロイ

1. AWS API MCP Serverのデプロイ

 基本的にはGitHubのDEPLOYMENT.md通りにやっていけば問題ありませんでしたが、私の場合最初にMarketplaceでサブスクライブしないといけないことを失念しており、数十分ハマりました。

image.png

Marketplaceで AWS API MCP Serverと調べたら出てくるので、購入(無料)しましょう。

2. Lambda (incident-invoke-agent) の実装

CloudWatch AlarmからSNSメッセージを受け取り、Bedrock Agent Runtimeのinvoke_agent APIを呼び出します。

Lambda (incident-invoke-agent)のソースコード
import boto3, json, uuid, os, logging
from botocore.config import Config

logger = logging.getLogger()
logger.setLevel(logging.INFO)

bedrock = boto3.client(
    'bedrock-agent-runtime',
    region_name='us-east-1',
    config=Config(read_timeout=800, connect_timeout=10)
)
sns = boto3.client('sns', region_name='us-east-1')

AGENT_ID = os.environ['AGENT_ID']
AGENT_ALIAS_ID = os.environ['AGENT_ALIAS_ID']
NOTIFY_TOPIC_ARN = os.environ['NOTIFY_TOPIC_ARN']

def lambda_handler(event, context):
    sns_message = json.loads(event['Records'][0]['Sns']['Message'])
    alarm_name = sns_message.get('AlarmName', 'Unknown')
    alarm_desc = sns_message.get('AlarmDescription', '')
    trigger_time = sns_message.get('StateChangeTime', '')

    prompt = (
        f"インシデントアラートが発火しました。\n"
        f"アラーム名: {alarm_name}\n"
        f"説明: {alarm_desc}\n"
        f"発火時刻: {trigger_time}\n\n"
        f"ロググループ /aws/lambda/test-error-generator の直近のエラーを調査し、原因と対処法を報告してください。"
    )

    session_id = f"incident-{uuid.uuid4().hex}"
    logger.info(f"Invoking agent for alarm: {alarm_name}")

    response = bedrock.invoke_agent(
        agentId=AGENT_ID,
        agentAliasId=AGENT_ALIAS_ID,
        sessionId=session_id,
        inputText=prompt
    )

    result = ""
    for ev in response['completion']:
        if 'chunk' in ev:
            result += ev['chunk']['bytes'].decode('utf-8')

    logger.info(f"Agent result: {result[:200]}")

    sns.publish(
        TopicArn=NOTIFY_TOPIC_ARN,
        Subject=f"[インシデント調査結果] {alarm_name}",
        Message=f"アラーム: {alarm_name}\n発火時刻: {trigger_time}\n\n--- 調査結果 ---\n{result}"
    )

注意ポイント

  • read_timeout=800 は重要。Agentが複数のAWSサービスを調査する場合、デフォルトの60秒では足りません。
  • session_id は一意である必要があります。

2. Lambda (mcp-gateway) の実装 - MCPプロトコル処理

Bedrock AgentとAgentCore Runtimeの橋渡しをする薄いプロキシレイヤーです。AWS API MCP ServerのMCPプロトコルを処理します。

Lambad (mcp-gateway)のソースコード
import json
import logging
import os
from typing import Any, Dict, Tuple, Optional

import boto3

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# ====== 必須:呼び出し先 AgentCore Runtime (AWS API MCP Server) の ARN ======
TARGET_AGENT_RUNTIME_ARN = os.environ["TARGET_AGENT_RUNTIME_ARN"]

MCP_PROTOCOL_VERSION = os.environ.get("MCP_PROTOCOL_VERSION", "2024-11-05")

DEFAULT_MCP_TOOL = os.environ.get("DEFAULT_MCP_TOOL", "call_aws")

ENABLE_TOOLS_LIST = os.environ.get("ENABLE_TOOLS_LIST", "false").lower() == "true"


_agentcore = boto3.client("bedrock-agentcore")


# -------------------------
# Bedrock Agents event utils
# -------------------------
def _normalize_params(event: Dict[str, Any]) -> Dict[str, Any]:
    out: Dict[str, Any] = {}

    for p in event.get("parameters", []) or []:
        name = p.get("name")
        if name:
            out[name] = p.get("value")

    body_props = (
        event.get("requestBody", {})
             .get("content", {})
             .get("application/json", {})
             .get("properties", [])
        or []
    )
    for p in body_props:
        name = p.get("name")
        if name:
            val = p.get("value")
            if name == "arguments" and isinstance(val, str):
                try:
                    val = json.loads(val)
                except (json.JSONDecodeError, TypeError):
                    # {key=value} 形式をパース
                    import re
                    kv = re.findall(r'(\w+)=([^,}]+)', val)
                    if kv:
                        val = {k.strip(): v.strip() for k, v in kv}
            out[name] = val

    return out


def _bedrock_ok(event: Dict[str, Any], payload_obj: Any, status_code: int = 200,
                session_attrs: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
    response_body = {
        "application/json": {
            "body": json.dumps(payload_obj, ensure_ascii=False)
        }
    }
    action_response = {
        "actionGroup": event["actionGroup"],
        "apiPath": event["apiPath"],
        "httpMethod": event["httpMethod"],
        "httpStatusCode": status_code,
        "responseBody": response_body,
    }
    return {
        "messageVersion": event.get("messageVersion", "1.0"),
        "response": action_response,
        "sessionAttributes": session_attrs if session_attrs is not None else event.get("sessionAttributes", {}) or {},
        "promptSessionAttributes": event.get("promptSessionAttributes", {}) or {},
    }


# -------------------------
# MCP over InvokeAgentRuntime
# -------------------------
def _invoke_agentcore_runtime(runtime_session_id: str, payload_obj: Dict[str, Any]) -> Dict[str, Any]:
    payload_bytes = json.dumps(payload_obj, ensure_ascii=False).encode("utf-8")

    resp = _agentcore.invoke_agent_runtime(
        agentRuntimeArn=TARGET_AGENT_RUNTIME_ARN,
        runtimeSessionId=runtime_session_id,
        payload=payload_bytes,
        accept="application/json, text/event-stream",
        contentType="application/json",
    )

    # contentType により取り出し方が変わる(JSON or event-stream)
    ctype = resp.get("contentType", "")
    body_bytes = b""

    # boto3 の StreamingBody は read() で全体を読む
    streaming_body = resp.get("response")
    if streaming_body is not None:
        body_bytes = streaming_body.read()

    if "text/event-stream" in ctype:
        # SSE: "data: {...}" 行から最後のJSONを取得
        last_json = b""
        for line in body_bytes.splitlines():
            line = line.strip()
            if line.startswith(b"data: "):
                last_json = line[6:]
        if last_json:
            body_bytes = last_json

    try:
        return json.loads(body_bytes.decode("utf-8"))
    except Exception:
        logger.warning("Non-JSON response from runtime. contentType=%s body=%s", ctype, body_bytes[:2000])
        return {"raw": body_bytes.decode("utf-8", errors="replace"), "contentType": ctype}


def _mcp_initialize(runtime_session_id: str, request_id: int) -> Tuple[Dict[str, Any], int]:
    init_req = {
        "jsonrpc": "2.0",
        "id": request_id,
        "method": "initialize",
        "params": {
            "protocolVersion": MCP_PROTOCOL_VERSION,
            "capabilities": {
                "tools": {"listChanged": False}
            },
            "clientInfo": {"name": "bedrock-agent-lambda", "version": "1.0.0"}
        }
    }
    init_resp = _invoke_agentcore_runtime(runtime_session_id, init_req)

    # initialized notification(通知なので id なし)[2](https://modelcontextprotocol.io/specification/2025-03-26/basic/lifecycle)
    notif = {
        "jsonrpc": "2.0",
        "method": "notifications/initialized",
        "params": {}
    }
    _invoke_agentcore_runtime(runtime_session_id, notif)
    # 1つのAgent Action = 1つのMCP RPC呼び出し
    # idを毎回インクリメントして、各リクエストを区別
    return init_resp, request_id + 1


def _mcp_tools_list(runtime_session_id: str, request_id: int) -> Dict[str, Any]:
    req = {"jsonrpc": "2.0", "id": request_id, "method": "tools/list", "params": {}}
    return _invoke_agentcore_runtime(runtime_session_id, req)


def _mcp_tools_call(runtime_session_id: str, request_id: int, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
    req = {
        "jsonrpc": "2.0",
        "id": request_id,
        "method": "tools/call",
        "params": {"name": tool_name, "arguments": arguments}
    }
    return _invoke_agentcore_runtime(runtime_session_id, req)


# -------------------------
# Main handler
# -------------------------
def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
    logger.info("event=%s", json.dumps(event, ensure_ascii=False))

    try:
        _ = event["actionGroup"]
        _ = event["apiPath"]
        _ = event["httpMethod"]
        session_id = event.get("sessionId", "no-session")

        params = _normalize_params(event)
        session_attrs = event.get("sessionAttributes", {}) or {}

        runtime_session_id = session_id if len(session_id) >= 33 else session_id + "-" + "0" * (33 - len(session_id) - 1)

        req_id = int(session_attrs.get("mcp_req_id", "1"))
        if session_attrs.get("mcp_initialized") != "true":
            init_resp, req_id = _mcp_initialize(runtime_session_id, req_id)
            session_attrs["mcp_initialized"] = "true"
            # init_resp を残したい場合
            session_attrs["mcp_server_info"] = json.dumps(init_resp.get("result", {}).get("serverInfo", {}), ensure_ascii=False)

        if ENABLE_TOOLS_LIST or "/list" in (event["apiPath"] or "").lower():
            req_id += 1
            tools_resp = _mcp_tools_list(runtime_session_id, req_id)
            session_attrs["mcp_req_id"] = str(req_id)
            return _bedrock_ok(event, {"mcp": tools_resp}, 200, session_attrs)

        args = params.get("arguments") or {}
        if isinstance(args, dict):
            cli_command = (
                args.get("cli_command")
                or args.get("command")
                or args.get("aws_cli")
                or args.get("cli")
                or params.get("cli_command")
                or params.get("command")
                or params.get("aws_cli")
                or params.get("cli")
            )
        else:
            cli_command = (
                params.get("cli_command")
                or params.get("command")
                or params.get("aws_cli")
                or params.get("cli")
            )

        if not cli_command:
            session_attrs["mcp_req_id"] = str(req_id)
            return _bedrock_ok(
                event,
                {
                    "ok": False,
                    "error": "Missing parameter. Provide one of: cli_command | command | aws_cli | cli",
                    "example": {"cli_command": "aws sts get-caller-identity"}
                },
                400,
                session_attrs
            )

        tool_name = params.get("tool_name") or DEFAULT_MCP_TOOL

        # AWS API MCP Server の call_aws は「AWS CLI コマンド」を入力として扱うツール
        req_id += 1
        # arguments dict をそのまま渡す(cli_command キーを含む)
        tool_arguments = args if isinstance(args, dict) and args else {"cli_command": cli_command}
        call_resp = _mcp_tools_call(
            runtime_session_id,
            req_id,
            tool_name,
            tool_arguments
        )

        session_attrs["mcp_req_id"] = str(req_id)

        # MCPの標準レスポンスは result / error を持つ [3](https://modelcontextprotocol.io/specification/2025-03-26/server/tools)
        if "error" in call_resp:
            return _bedrock_ok(event, {"ok": False, "mcp_error": call_resp["error"], "raw": call_resp}, 500, session_attrs)

        return _bedrock_ok(event, {"ok": True, "mcp": call_resp}, 200, session_attrs)

    except KeyError as e:
        return _bedrock_ok(event if isinstance(event, dict) else {}, {"ok": False, "error": f"Missing required field: {str(e)}"}, 400)

    except Exception as e:
        logger.exception("Unexpected error: %s", e)
        return _bedrock_ok(event if isinstance(event, dict) else {}, {"ok": False, "error": "Internal server error"}, 500)

3. Bedrock Agent

先ほど作ったLambdaをAction Groupとして利用して、インシデント調査を行うようなエージェントにします。
今回は次のようなプロンプトを設定しました。

Agentのプロンプト
あなたは AWS 環境におけるインシデント調査を担当する自律的な調査エージェントです。

あなたの主な役割は、インシデントに対して仮説を立て、
MCP ツールを使って証拠を集め、その結果を解釈し、
次に何を調査すべきかを自分で判断することです。

## 基本原則(必ず守ること)

- 必ず「考えてから」行動してください
- ツールを呼ぶ前に、必ず仮説を立ててください
- 仮説は証拠によって検証・否定してください
- 次に取るべき調査ステップは必ずあなた自身で決定してください
- 証拠なしに原因を断定してはいけません
- Action Group(ツール実行部)には判断能力がない前提で行動してください

## ツール利用方針(重要)

あなたが利用できるツールは、MCP ツールを呼び出すための
「薄いプロキシ(Action Group)」のみです。

このプロキシは以下の特性を持ちます:
- MCP ツールを実行するだけ
- 結果を解釈しない
- 妥当性チェックや制御は行わない

したがって、
**すべての推論・判断・解釈はあなたの責任です。**

あなたは MCP ツールを使って、次のような情報を取得できます:
- EC2、ELB、ECS、EKS、RDS、S3、IAM などの AWS リソース情報
- CloudWatch のメトリクスやログ
- CloudTrail のイベント履歴
- GuardDuty や Security Hub の検出結果

明示的に指示されない限り:
- すべての操作は READ ONLY として扱ってください
- 復旧操作や設定変更は行ってはいけません

## インシデント調査の進め方(必須ループ)

次の手順を必ず繰り返してください:

1. **事象の整理**
   - 観測されている症状を言語化する(例:5xx エラー増加、レイテンシ上昇)

2. **仮説の立案**
   - 起こり得る原因を1つ以上挙げる

3. **次に集める証拠の決定**
   - 仮説を検証するために必要な情報を考える
   - 1ステップにつき、呼ぶ MCP ツールは必ず 1 つにする

4. **ツールの実行**
   - Action Group を使って MCP ツールを呼び出す

5. **結果の解釈**
   - 結果が何を示しているか説明する
   - 仮説が支持されたか、否定されたかを明確にする

6. **仮説の更新**
   - 仮説を修正、分割、または差し替える

7. **結論が妥当になるまで繰り返す**

## 回答スタイル

- 以下を明確に分けて記載してください:
  - 仮説
  - 取得した証拠
  - 解釈
  - 結論
- 不確実な点は不確実だと明記してください
- 推測よりも事実を優先してください
- SRE チームに報告するつもりで説明してください

## セッション意識

- 1つのインシデント = 1つの調査セッションとして扱ってください
- 複数のツール実行結果を論理的に関連付けてください
- ツールがステートレスでも、調査の流れはあなたが維持してください

## 最終アウトプット(必須)

調査の最後に、必ず以下を提示してください:

- 調査結果の要約
- 最も可能性の高い原因(断定できない場合は候補)
- それぞれの結論を支える証拠
- 人間が次に取るべきアクション案(あれば)

あなたは単なるコマンド実行者ではありません。
あなたは調査員です。

重視したポイント

  • 「仮説を立ててから行動する」というループを明示的に指示
  • READ ONLYを二重に制限(IAMロール + プロンプト)
  • 出力フォーマットを明確に指定

また、アクショングループに先ほど作成したmcp-gatewayとなるLambdaを指定して、次のようなスキーマを定義します。

OpenAPIスキーマ
openapi: 3.0.3
info:
  title: MCP Proxy Action Group API
  version: 1.0.0
  description: >
    Thin proxy API for calling MCP tools deployed on Amazon Bedrock AgentCore Runtime.
    This API is intended to be used as an Amazon Bedrock Agents Action Group schema.

paths:
  /mcp/tools/call:
    post:
      operationId: callMcpTool
      summary: Call an MCP tool (thin proxy)
      description: >
        Proxies a single MCP tool call to an MCP server hosted on AgentCore Runtime.
        The implementation should not interpret results; it returns raw MCP tool output.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/McpToolCallRequest"
      responses:
        "200":
          description: MCP tool call succeeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/McpToolCallResponse"
        "400":
          description: Invalid request (missing required fields / invalid JSON)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Proxy or upstream error (AgentCore Runtime / MCP server failure)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

components:
  schemas:
    McpToolCallRequest:
      type: object
      additionalProperties: false
      required:
        - tool_name
      properties:
        tool_name:
          type: string
          description: >
            MCP tool name to call (e.g., call_aws, suggest_aws_commands, etc.).
        arguments:
          type: object
          description: >
            Tool arguments as a JSON object. Passed through to the MCP server.
          additionalProperties: true
        session_id:
          type: string
          description: >
            Optional incident/session identifier. If provided, use it to maintain continuity
            across calls (e.g., map to MCP session header).
        timeout_ms:
          type: integer
          format: int32
          minimum: 1
          maximum: 900000
          default: 60000
          description: Optional timeout for the proxy execution.

    McpToolCallResponse:
      type: object
      additionalProperties: true
      required:
        - ok
        - tool_name
      properties:
        ok:
          type: boolean
          description: Whether the proxy call succeeded.
        session_id:
          type: string
          nullable: true
          description: Echo of request session_id if provided.
        tool_name:
          type: string
          description: Echo of request tool_name.
        result:
          description: Raw tool execution result returned by the MCP server.
          oneOf:
            - type: object
            - type: array
            - type: string
            - type: number
            - type: boolean
            - type: "null"
        metadata:
          type: object
          description: Optional metadata for debugging/tracing.
          additionalProperties: true

    ErrorResponse:
      type: object
      additionalProperties: false
      required:
        - ok
        - error
      properties:
        ok:
          type: boolean
          enum: [false]
        error:
          type: object
          additionalProperties: true
          required:
            - message
          properties:
            message:
              type: string
            code:
              type: string
              nullable: true
            details:
              type: object
              nullable: true
              additionalProperties: true

動作イメージ

 今回は、Errorを起こすために用意したLamdbaのロググループにメトリクスフィルタを仕掛けて、メールに調査結果を飛ばしてみました。
 Lambdaを動作させてしばらく待っていると、次のようなメールが来ました。

タイトル:[インシデント調査結果] IncidentErrorAlarm

メール本文:
アラーム: IncidentErrorAlarm
発生時刻: 2026-03-19T08:20:19.325+0000

--- 調査結果 ---
## インシデント調査結果報告

### 事象の整理
- **アラーム名**: IncidentErrorAlarm
- **発生時刻**: 2026-03-19T08:20:19.325+0000
- **対象**: Lambda関数 `test-error-generator` でERRORログを検知

### 仮説と証拠収集

**初期仮説**: Lambda関数の実行エラーが複数回発生している

### 取得した証拠

1. **CloudWatchログの分析結果**:
   - 複数のエラーログを確認
   - エラーメッセージ: `ERROR: テスト用エラーが発生しました - something went wrong`
   - 発生時刻パターン:
     - 2026-03-19T08:16:37.962Z (RequestId: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)
     - 2026-03-19T08:17:45.537Z (RequestId: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX) 
     - 2026-03-19T08:17:46.266Z (RequestId: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)
     - 2026-03-19T08:17:46.990Z (RequestId: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)

2. **Lambda関数の設定情報**:
   - 関数名: `test-error-generator`
   - ランタイム: Python 3.12
   - メモリ: 128 MB
   - タイムアウト: 3秒
   - 実行ハンドラー: `lambda_code.lambda_handler`
   - 状態: Active
   - 最終更新: 2026-03-19T08:16:31.000+0000

3. **実行メトリクス分析**:
   - すべての実行が正常に終了 (END RequestId が確認)
   - メモリ使用量: 36 MB (128 MB中)
   - 実行時間: 1.71ms~17.99ms (3秒タイムアウト以内)
   - リソース不足やタイムアウトは未発生

### 解釈と結論

**最も可能性の高い原因**: **意図的なテスト用エラー生成**

**根拠**:
1. エラーメッセージに「テスト用エラーが発生しました」と明記されている
2. 関数名が `test-error-generator` でありテスト目的を示唆
3. 全実行が正常終了しており、システム障害ではない
4. エラーログのパターンが一定で、プログラムによる意図的な生成と推測

**確定できない点**:
- 実際のソースコードの内容
- エラー生成がスケジュール実行か手動実行かの区別
- テストが完了したかどうかの状況

### 推奨されるアクション

1. **即座に必要な対応**: 
   - 開発チームに連絡し、テストの実行状況と完了予定を確認
   - 本番環境への影響がないことを再確認

2. **中期的な改善**:
   - テスト用Lambda関数には明確なタグ付けや命名規則の適用
   - テスト実行時の事前通知プロセスの確立
   - テスト用エラーログには専用のログレベルやプレフィックスの使用を検討

3. **監視の調整**:
   - テスト用リソースを本番監視から除外するか、別アラートルールの適用
   - 緊急度の分類(テスト vs 本番)の明確化

**結論**: これは本番障害ではなく、意図的なテスト用エラー生成と判断されます。システムリソースやアプリケーションの実際の障害ではありません。

意図的に起こしたエラーであることが完全にバレバレなものにしているので、ずばりその旨を言い当てています。
実際にAPIをたたいて環境を見に行って、Lambdaの設定情報などを拾ってきているので、意図している動作はしていそうです。

さいごに

 本記事では、Amazon Bedrock AgentとAWS API MCP Serverを組み合わせることで、CloudWatch Alarmをトリガーとして自動インシデント調査を行う仕組みを紹介しました。

 最初は「LLMにツール呼び出し能力があれば、AWSリソースを自動調査できるだろう」と単純に考えていたのですが、実装してみると、MCPプロトコルのライフサイクル、セッションIDの制限、タイムアウト設定など、細かなつまづきポイントがたくさんあり、面白かったです。

今回はインシデント調査の自動化をテーマにしてみましたが、AWS環境の定期的なセキュリティチェックや、コスト最適化の推奨なども、プロンプト次第で自動化できる可能性はあると考えています。

 ぜひ、このシステムをベースにカスタマイズして、みなさんのユースケースに適したAI自動システムを構築してください。

参考資料

リソース 説明
Amazon Bedrock Agents ドキュメント 公式仕様とAPI リファレンス
Amazon Bedrock AgentCore Runtime ドキュメント MCPサーバーのマネージドサービス仕様
AWS API MCP Server (GitHub) AWS公式MCPサーバー実装
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?