/ AWS, GENAI, LAB

CloudWatch MCP × Strands × MCP Apps:
Bedrock 운영 Cockpit 만들기

Codex 같은 coding agent와 직접 대화하면 이미 많은 일을 할 수 있습니다. 그렇다면 굳이 NemoClaw 같은 개인·조직용 Claw를 두고, 그 위에 별도의 운영 화면까지 만들어야 할까요?

이번 실험에서 내린 답은 단순합니다. 한 번의 개발 작업은 agent와 직접 대화하는 편이 빠르지만, 반복되는 운영 판단을 팀의 방식으로 고정하려면 agent 바깥의 실행 표면이 필요합니다. 어떤 데이터를 읽을지, 어떤 계산은 코드로 확정할지, 어떤 판단만 agent에게 맡길지, 결과를 누가 어떻게 검토할지를 하나의 workflow로 묶어야 하기 때문입니다.

이 질문을 Amazon Bedrock 운영으로 좁혀봤습니다. 실제 CloudWatch 메트릭은 AWS Labs CloudWatch MCP Server로 읽고, Strands는 제공된 근거만 해석하며, MCP Apps는 Overview·Investigate·Optimize를 오가는 운영 Cockpit을 렌더링하게 했습니다.


🦞 Agent와 직접 대화하면 안 되나?

됩니다. 다음과 같은 일은 Codex에게 바로 요청하는 편이 자연스럽습니다.

이 저장소 테스트를 실행하고 실패 원인을 고쳐줘.
최근 24시간 Bedrock 호출량을 조회해줘.
이 CloudWatch 메트릭을 보고 지연 원인 후보를 정리해줘.

하지만 운영 질문이 반복되면 매번 prompt와 tool 범위를 다시 설명해야 합니다. 사람마다 다른 명령을 사용하고, agent가 읽은 데이터 범위도 달라집니다. 결과가 text로만 남으면 시간 범위와 모델을 바꿔 비교하거나, 같은 context로 조사와 최적화를 이어가기도 불편합니다.

Claw는 더 똑똑한 단일 agent라기보다 여러 agent·tool·channel을 조직의 실행 규칙으로 감싸는 control plane에 가깝습니다.

직접 agent 대화 Claw 또는 운영용 agent surface
한 번의 개발 작업에 빠름 반복 workflow와 공유 context에 적합
사용자가 prompt와 tool을 직접 조합 허용 도구, 입력 스키마, 승인 흐름을 고정
대화 결과가 중심 dashboard, 알림, 기록, 후속 action까지 연결
개인의 숙련도에 크게 의존 팀의 운영 방식을 제품화 가능

따라서 나만의 Claw가 항상 필요한 것은 아닙니다. 단발 작업과 개인 개발에는 direct CLI가 충분합니다. 반면 반복되는 운영 업무, 여러 사람이 함께 보는 판단, 읽기·쓰기 권한 분리, 감사 가능한 실행 기록이 중요해지면 Claw의 가치가 생깁니다.

이번 PoC는 NemoClaw 자체를 설치한 실험은 아닙니다. 대신 Claw가 가져야 할 운영 surface를 MCP Apps로 작게 구현해보는 데 목적을 뒀습니다.


🧩 MCP Apps는 일반 MCP와 무엇이 다른가?

일반적인 MCP tool은 text나 structured data를 반환합니다. 데이터 조회와 action 실행에는 충분하지만, chart·form·dashboard처럼 사용자가 직접 조작해야 하는 결과에는 한계가 있습니다.

MCP Apps 공식 문서는 MCP server가 interactive UI를 표준 방식으로 전달하도록 MCP를 확장합니다. stable specification 2026-01-26 기준 핵심 흐름은 다음과 같습니다.

1. Tool이 ui:// resource를 metadata로 선언
2. Host가 tool을 호출
3. Host가 HTML resource를 읽어 sandboxed iframe에 렌더링
4. Host가 tool result를 UI에 전달
5. UI는 host를 통해 다시 tool을 호출

즉 MCP Apps API 문서는 별도의 dashboard SaaS API가 아닙니다. App 개발자에게는 App, React hook과 host communication API를, MCP server 개발자에게는 tool과 ui:// resource를 연결하는 API를, host 개발자에게는 sandboxed view를 임베드하는 bridge를 제공합니다.

이번 앱은 하나의 ui://bedrock-ops/cockpit.html resource를 세 tool에 연결했습니다.

Tool 역할
get_bedrock_overview 모델별 호출·토큰·지연·오류·캐시·TPM 요약
investigate_bedrock_model 이상 후보, 가능한 설명, 다음 확인 항목
optimize_bedrock_usage 비용·지연·균형 목표별 개선 기회와 검증 절차

UI는 tool의 대체물이 아닙니다. tool의 typed contract 위에 사람이 결과를 비교하고 다음 action을 선택할 수 있는 surface를 더한 것입니다.


🏗️ CloudWatch MCP와 Strands를 어떻게 나눴나?

구조는 다음과 같습니다.

MCP Apps basic-host
  ↓ Streamable HTTP / MCP
Bedrock Operations Cockpit Server
  ├─ Overview Service ───────────────┐
  ├─ Investigation Service ─ Strands │
  └─ Optimization Service ── Strands │
                                     ↓
CloudWatch Gateway ─ stdio MCP ─ AWS Labs CloudWatch MCP Server
                                     ↓
                            Amazon CloudWatch / AWS/Bedrock

Strands Agent ───────────────── Amazon Bedrock

AWS Labs CloudWatch MCP Server는 CloudWatch metrics, logs, alarms를 다루는 MCP tool을 제공합니다. 이번 환경에는 Bedrock 관련 log group과 alarm이 없어 AWS/Bedrock metric에만 집중했습니다. 서버가 제공하는 모든 기능을 그대로 열지 않고 get_metric_data, get_metric_metadata 두 read-only tool만 gateway allowlist에 넣었습니다.

Strands에는 CloudWatch 원본 응답을 그대로 넘기지 않았습니다. 애플리케이션이 먼저 다음 작업을 수행합니다.

  1. 정해진 metric 11개를 조회하고 timestamp와 값을 정규화합니다.
  2. 누락된 metric은 0이 아니라 unavailable로 표시합니다.
  3. Overview 합계와 평균을 결정론적으로 계산합니다.
  4. 중앙값과 MAD로 이상 후보를 찾습니다.
  5. Strands에는 정규화된 evidence와 허용된 enum만 전달합니다.
  6. agent 출력은 Zod schema로 다시 검증합니다.

이 분리가 중요합니다. LLM이 invocation 수나 latency를 만들어내지 못하게 하고, agent는 가능한 설명·반증 조건·검증 순서를 작성하는 일에만 사용했습니다. Strands structured output 문서가 설명하는 schema 기반 결과를 적용한 이유도 여기에 있습니다.


🧪 구현하며 확인한 경계

프로젝트는 TypeScript 단일 저장소로 구성했습니다.

React MCP App
@modelcontextprotocol/ext-apps 1.7.5
@modelcontextprotocol/sdk 1.30.0
@strands-agents/sdk 1.11.2
Vitest + Zod + Recharts
CloudWatch MCP Server via uvx

AWS profile보다 명시적인 임시 세션을 전달하기

MFA와 role chaining이 있는 AWS profile은 AWS CLI preflight가 성공해도, CloudWatch MCP의 boto3가 같은 profile을 다시 해석하면서 MFA를 한 번 더 요구할 수 있었습니다.

그래서 controller shell에서 임시 session credential을 내보내고, 세 값이 모두 존재할 때는 CloudWatch MCP 요청의 profile_name과 child process의 AWS_PROFILE을 제거했습니다.

eval "$(aws configure export-credentials --profile <aws-profile> --format env)"
AWS_REGION=us-west-2 npm run smoke:live

credential 값은 파일과 로그에 저장하지 않았습니다. 일부 값만 존재하면 profile fallback을 유지하고, 인증 오류는 원문 대신 AUTH_EXPIRED 같은 안정적인 code로 변환했습니다.

실제 MCP response shape를 fixture로 만들기

처음 live query는 metric 11개가 모두 unavailable로 표시됐습니다. CloudWatch에는 데이터가 있었기 때문에 응답 경계를 최소 구조로 확인했습니다.

Metrics Insights에서 GROUP BY ModelId를 사용하면 각 metricDataResultslabel에는 metric name이 아니라 model ID가 들어갔습니다.

{
  "metricDataResults": [
    {
      "id": "m1",
      "label": "<model-id>",
      "statusCode": "Complete",
      "datapoints": [
        { "timestamp": "<iso-time>", "value": 1 }
      ]
    }
  ]
}

기존 normalizer는 label을 metric name으로만 해석해 모든 series를 버리고 있었습니다. 조회한 metric을 explicit context로 전달하고, grouped query에서만 label을 model ID로 해석하도록 수정했습니다. 특정 모델을 dimension으로 직접 조회할 때의 설명형 label과 혼동하지 않도록 두 경로를 분리했습니다.

이 경험은 MCP tool을 붙일 때 문서의 type만 보는 것으로 충분하지 않다는 점을 보여줍니다. 실제 provider와 query mode가 만드는 serialization shape를 작은 fixture로 고정해야 합니다.

공식 basic-host를 처음 실행했을 때는 MCP App iframe이 404로 열리지 않는 문제도 만났습니다. host checkout을 .vendor/ext-apps에 둔 것이 원인이었습니다. Express sendFile이 절대 경로 중 .vendor를 dotfile 구간으로 판정해, 실제로 존재하는 sandbox.html을 기본 정책으로 차단한 것입니다. 공식 host 코드는 수정하지 않고 checkout을 비숨김 vendor/ext-apps로 옮겼고, sandbox endpoint의 200 응답과 세 workspace 렌더링을 다시 검증했습니다.


📊 실제 계정에서 확인한 결과

미국 서부 Oregon 리전의 실제 AWS/Bedrock metric을 대상으로 최근 24시간 smoke workflow를 실행했습니다. 식별자와 credential은 결과에서 제거했습니다.

단계 상태 관찰 결과 실행 시간
Overview complete 11개 모델, 11개 metric, warning 0 13.9초
Investigate partial 이상 후보 42개, finding 19개, next check 16개 26.0초
Optimize partial 개선 기회 6개 18.3초

Overview에서 확인한 metric은 다음과 같습니다.

Invocations
InvocationLatency
TimeToFirstToken
InvocationClientErrors
InvocationServerErrors
InputTokenCount
OutputTokenCount
CacheReadInputTokenCount
CacheWriteInputTokenCount
EstimatedTPMQuotaUsage
LegacyModelInvocations

Investigate와 Optimize 모두 Strands structured output 검증을 통과했습니다. 다만 partial은 실패가 아니라 일부 metric warning을 결과에 포함했다는 뜻입니다.

42개 이상 후보를 42개 장애로 해석하면 안 됩니다. MAD 규칙이 찾은 통계적 변화 후보이며, workload의 정상적인 burst도 포함될 수 있습니다. Strands가 이를 원인으로 확정하지 않고 확인할 metric과 반증 조건을 함께 내놓게 한 이유입니다.

자동 테스트는 schema, gateway, metric normalization, anomaly detection, service, MCP server, UI, live smoke contract, host launcher를 포함해 123개가 통과했습니다. MCP Apps 공식 basic-host v1.7.5의 이중 iframe 안에서 Overview 11개 모델, Investigate 41개 anomaly register, Optimize 6개 validation-ready move가 실제 계정 데이터로 렌더링되는 것도 확인했습니다.


⚠️ 이번 실험의 제한

첫째, 현재 계정에는 Bedrock 관련 CloudWatch Logs와 alarm이 없어 metric 기반 조사만 수행했습니다. application log, deployment event, trace까지 연결한 RCA는 다음 단계입니다.

둘째, 최적화 기회는 token·cache·latency·error·quota signal을 기반으로 합니다. 계약 할인, 리전별 가격, 실제 billing data가 없으므로 달러 절감액을 표시하지 않았습니다.

셋째, MCP Apps host support는 client마다 다릅니다. MCP Apps는 core MCP의 optional extension이므로 host가 capability를 지원해야 합니다. 이번 검증은 공식 repository의 reference basic-host를 사용했습니다.

넷째, 이 결과는 최근 24시간이라는 한 window의 관찰입니다. production 운영에는 주기적 baseline, 배포 annotation, 장기 저장, 사용자별 권한과 approval flow가 추가로 필요합니다.


⌨️ 기업은 결국 CLI를 잘 설계해야 한다

이번 실험에서 UI보다 먼저 안정화해야 했던 것은 CLI와 headless process 경계였습니다. CloudWatch MCP도, Strands도, MCP Apps host도 결국 automation 가능한 process로 연결됩니다.

기업용 agent workflow에서 CLI는 단순한 개발자 편의 기능이 아닙니다. agent와 platform을 이어주는 최소 실행 계약입니다.

CLI 설계 원칙 필요한 이유
non-interactive mode agent와 CI가 prompt 없이 실행 가능해야 함
structured input/output text scraping 대신 schema로 연결해야 함
stable exit code와 error code 재시도·중단·인증 갱신을 구분해야 함
explicit auth boundary profile, temporary session, role 전달 규칙이 명확해야 함
read/write tool separation 조회와 변경의 권한·승인 경계를 나눠야 함
bounded timeout과 cancellation child process와 session을 확실히 회수해야 함
redaction과 audit metadata 비밀은 숨기고 실행 근거는 남겨야 함
version pinning host·protocol·serialization 변화에 재현성을 확보해야 함

UI는 바뀔 수 있고 agent도 교체될 수 있습니다. 반면 잘 설계된 CLI와 protocol contract가 있으면 Codex, 다른 coding agent, Claw gateway, CI가 같은 capability를 재사용할 수 있습니다.

기업의 agent 전략은 어떤 모델을 쓰느냐만의 문제가 아니라, 기존 업무 capability를 얼마나 안전하고 조합 가능한 CLI로 노출하느냐의 문제에 더 가깝습니다.


Outro

Codex와 직접 대화하는 방식은 여전히 가장 빠른 출발점입니다. 이번 실험도 agent에게 shell command를 직접 시키는 단계에서 시작했습니다.

하지만 반복되는 Bedrock 운영 질문을 세 workspace로 고정해보니 Claw가 필요한 지점이 더 분명해졌습니다. CloudWatch MCP가 사실을 가져오고, 결정론적 코드가 수치를 확정하고, Strands가 근거 안에서 설명하며, MCP Apps가 사람이 검토하고 다음 action을 선택하는 표면을 만들었습니다.

다음 단계는 CloudWatch Logs, deployment context, alarm과의 연결입니다. 그때도 핵심은 더 많은 tool을 무작정 여는 것이 아니라, 읽기 범위와 판단 근거, 변경 approval을 contract로 고정하는 것입니다.


References