refs #732: add dynamic HMM HTML report MCP tool

This commit is contained in:
devmrko
2026-08-10 15:23:36 +09:00
parent cc2c3e3e25
commit c0c5a36ba0
12 changed files with 747 additions and 39 deletions

View File

@@ -0,0 +1,39 @@
# HMM HTML 리포트 MCP
## 목적
선사 실적 Federation 조회의 구조화 결과를 HMM 기업 리포트 HTML로 표현한다. 리포트 도구는
DB를 다시 조회하거나 자연어를 해석하지 않는다.
## 결정사항
- MCP 도구 `render_hmm_carrier_report``reportJson` 문자열 하나를 입력으로 받는다.
- 입력은 질문·답변 근거·조회 행으로 구성된 허용 JSON 계약이며, Oracle DB 함수는 템플릿에만 매핑한다.
- 선사 실적 조회 MCP가 VPD를 적용한 데이터 접근 경계이고, 리포트 MCP는 표현 경계다.
- 승인된 HTML 템플릿은 DB CLOB으로 버전 관리하고, 생성 HTML은 MCP 응답의 `html` 속성으로만 반환한다.
## 전체 흐름
```text
HMM 포털 → `https://hmm-backoffice.cloud-handson.com/mcp` → 데이터 조회 도구
→ 포털의 범용 결과 정규화 → 이전 결과 입력형 렌더링 도구 → HTML 미리보기
```
리포트에 보이는 값은 조회 결과 행에서 계산되므로, 자연어 답변과 별개로 재해석되지 않는다.
## 문서 지도
- [아키텍처와 입력 계약](architecture.md)
- [적용·검증 절차](cookbook.md)
- [문제 해결](troubleshooting.md)
## 현재 상태
Oracle DB의 custom Agent Tool과 템플릿 CLOB은 적용됐다. 포털은 리포트·차트·HTML 요청에서
도구 이름을 고정하지 않고, 발견한 도구의 설명·입력 스키마를 기준으로 데이터 조회 뒤 렌더링을
순차 호출하도록 운영 배포한다.
2026-08-10 운영 검증에서 `tools/list`, `reportJson` 입력, 64KB 입력 한도, 동적 JSON 매핑과
Agent 2단계 실행을 확인했다. HTML 템플릿에는 업무 샘플 행을 저장하지 않으며, 호출 시 전달된
`report``rows`만 표시한다. 선행 조회가 0건을 반환하면 빈 리포트를 생성하는 것이 정상이며,
조회 결과나 권한 설정은 이 기능의 변경 범위가 아니다.

View File

@@ -0,0 +1,72 @@
# 아키텍처와 입력 계약
## 책임 경계
| 구성요소 | 책임 | DB 접근 |
|---|---|---|
| `search_carrier_performance` | Bearer 기반 사용자 식별 및 VPD 적용 선사 조회 | 허용 |
| AI 웹 콘솔 | 도구 계약을 판별해 조회 결과를 정규화하고 렌더러를 후속 호출 | 없음 |
| `render_hmm_carrier_report` | DB CLOB 템플릿에 허용된 JSON을 매핑 | 템플릿만 읽음 |
| 브라우저 | 반환 HTML 표시·다운로드 | 없음 |
```text
HMM portal ──► /mcp (Bearer) ──► Backoffice MCP facade ──► DB Agent Tool
VPD applied rows
Console
report MCP tool call
HMM_REPORT_TEMPLATES
HTML 응답
```
리포트 MCP도 같은 Bearer 인증을 통과해야 하지만, 권한 판단은 조회 MCP에서 끝난다. 리포트
입력의 임의 사용자 ID를 신뢰하거나 DB를 재조회하지 않는다.
## 포털의 범용 순차 호출 규칙
포털은 `render_hmm_carrier_report`라는 이름을 조건문에 넣지 않는다. 발견한 MCP 도구가 아래
계약을 동시에 보이면 **이전 결과 입력형 렌더러**로 분류한다.
- 입력 스키마에 `reportJson`·`report_payload`처럼 리포트 JSON/payload를 받는 문자열이 있다.
- 설명에 HTML/리포트/렌더링 의도와 `조회 결과`, `후속 처리`, `already-authorized`처럼 선행 결과를
사용한다는 의도가 있다.
사용자가 리포트·보고서·대시보드·차트·HTML을 요청하고 이 렌더러가 발견되면, 기존 LLM 라우터는
렌더러를 제외한 데이터 도구 중 하나를 먼저 선택한다. 첫 응답의 `items` 또는 `results`
camelCase JSON으로 정규화해 두 번째 도구에 전달한다. 포털은 렌더러의 `html` 응답을 iframe으로
표시한다. 이 규칙은 동일 계약을 선언하는 다른 업무 리포트 도구에도 적용된다.
## `reportJson` 계약
최상위에는 `report` 객체와 `rows` 배열만 허용한다. `report`에는 `id`, `category`, `title`,
`generatedAt`, `requestedBy`, `question`, `answer`, `execution`, `evidence`, `limitation`
넣는다. 행에는 담당자·선사 식별자와 최신 KPI만 넣는다.
서버는 JSON 크기, 행 수, 문자열 길이, 숫자 형식을 제한하고, 템플릿에 주입할 JSON에서
`</script>`를 이스케이프한다. payload는 저장하지 않는다.
## 배포 도구 계약
`BACKOFFICE_MCP_TOOLS`에 다음과 같이 등록한다. 실제 환경 변수에는 비밀값을 넣지 않는다.
```json
{
"name": "render_hmm_carrier_report",
"label": "HMM 선사 실적 HTML 리포트",
"description": "VPD 적용 선사 실적 조회 결과를 HMM HTML 리포트로 표현합니다.",
"argumentName": "reportJson",
"argumentDescription": "정규화된 선사 실적 리포트 JSON입니다.",
"executionType": "AGENT_TOOL",
"targetName": "HMM_CARRIER_REPORT_RENDERER",
"targetParameterName": "P_REPORT_JSON"
}
```
`targetName`은 DBMS_CLOUD_AI_AGENT custom tool `HMM_CARRIER_REPORT_RENDERER`다.
템플릿에는 `const reportData = __REPORT_DATA__;` 자리만 둔다. DB 함수는 호출 시 전달받은 JSON을
이 자리에 삽입하고, 조회 Tool을 호출하거나 기본 업무 행을 보충하지 않는다.

View File

@@ -0,0 +1,45 @@
# 적용·검증 절차
## 1. 준비
- `vpd-backoffice` 배포본에 HTML 템플릿과 HMM CI 리소스가 포함되어야 한다.
- 조회 도구 `search_carrier_performance`가 구조화된 `items` 배열을 반환해야 한다.
- 배포 환경의 `BACKOFFICE_MCP_TOOLS``AGENT_TOOL` 도구 계약을 추가한다.
먼저 `database/adb/83_hmm_carrier_html_report_tool.sql`을 실행한 뒤 다음 명령으로 현재
승인 템플릿을 CLOB에 적재한다. 스크립트는 템플릿을 UTF-8 Base64로 복원한다.
```bash
./scripts/load-hmm-carrier-report-template.sh
```
## 2. 확인
1. 동일 Bearer token으로 `tools/list`를 호출한다.
2. `render_hmm_carrier_report`와 입력 속성 `reportJson`이 보이는지 확인한다.
3. 먼저 `search_carrier_performance`를 호출해 `items`를 얻는다.
4. 질문·답변 근거·items를 reportJson으로 구성해 리포트 도구를 호출한다.
5. 응답 `response.html`을 새 탭 또는 sandboxed iframe에서 연다.
성공 판정은 제목, 담당자별 막대 차트, 위험 분포, 상세 표가 `rows`와 일치하는 것이다.
템플릿 내부에 `__REPORT_DATA__`가 남지 않고, 검증 payload의 담당자·선사 식별자가 반환 HTML에
포함되는지도 확인한다.
## 3. 포털 순차 실행 확인
1. 포털 MCP 설정의 endpoint를 `https://hmm-backoffice.cloud-handson.com/mcp`로 설정하고,
허용 목록에 조회 도구와 리포트 렌더링 도구를 모두 넣는다.
2. 포털에서 예를 들어 `E1001 팀의 선사 최신 실적을 HMM 리포트로 만들어줘`라고 요청한다.
3. 실행 상세에서 첫 단계가 데이터 조회이고 두 번째 단계가 HTML 렌더링인지 확인한다.
4. 결과 영역에 `생성된 리포트` iframe이 표시되고, 막대·상세 표가 첫 단계 `items`와 일치하는지
확인한다.
첫 단계가 0건이면 두 번째 단계의 `reportJson.rows`도 빈 배열이어야 한다. 이 경우 순차 실행과
HTML 생성은 성공한 것이며, 데이터 조회 문제는 리포트 Tool과 분리해 확인한다.
실패 시 [문제 해결](troubleshooting.md)의 `텍스트만 답하고 리포트가 생성되지 않음`을 따른다.
## 4. 롤백
`BACKOFFICE_MCP_TOOLS`에서 리포트 도구 항목을 제거하고 서비스를 재기동하면 기존 조회 MCP에는
영향 없이 리포트 후속 호출만 중단된다. 템플릿은 DB나 사용자 데이터에 변경을 만들지 않는다.

View File

@@ -0,0 +1,50 @@
# 문제 해결
## 텍스트만 답하고 리포트가 생성되지 않음
**원인**: 포털이 단일 조회만 실행했거나, MCP discovery 결과에 이전 결과 입력형 렌더러가 없다.
**확인**:
1. 포털 설정 endpoint가 `https://hmm-backoffice.cloud-handson.com/mcp`인지 확인한다.
2. `tools/list` 결과에 조회 도구와 `reportJson` 입력을 가진 HTML/리포트 렌더러가 모두 있는지 확인한다.
3. 포털 실행 상세에서 실행 방식이 `agent`, Agent 단계가 두 번인지 확인한다.
**해결**: endpoint와 허용 목록을 함께 갱신한 뒤 포털 서비스를 재기동한다. 렌더러 설명에는
`조회 결과 후속 처리`처럼 선행 결과를 받는다는 문구를 유지한다.
**재발 방지**: 새 업무 리포트 도구도 `reportJson`류 입력과 후속 처리 의도를 설명에 선언한다.
포털 코드에 특정 도구명을 추가하지 않는다.
## 렌더러가 입력 형식 오류를 반환함
**원인**: 조회 도구가 `items`/`results` 배열을 반환하지 않거나, 렌더러가 요구하는 payload 계약과
템플릿 계약이 다르다.
**확인**: 첫 Agent 단계의 응답에 행 배열이 있는지, 두 번째 단계 arguments에 `report``rows`
있는지 확인한다. 비밀값·Bearer token은 실행 상세에 남기지 않는다.
**해결**: 조회 도구는 구조화 행 배열을 반환하고, 렌더러 함수는 `report``rows` 계약을 유지한다.
## 생성된 HTML이 화면에 표시되지 않음
**원인**: 렌더러 응답에 `html` 문자열이 없거나, HTML이 유효하지 않다.
**확인**: 두 번째 MCP 응답의 `response.html` 존재와 길이를 확인한다.
**해결**: DB 템플릿 활성 상태와 custom Agent Tool의 반환 형식을 확인한다. 포털은 유효한 `html`
또는 `rendered_html`만 sandboxed iframe으로 표시한다.
## 리포트는 생성됐지만 행이 0건임
**원인**: 렌더러 앞에서 실행된 데이터 조회 Tool이 0건을 반환했다. 렌더러는 없는 행을 만들거나
기본 샘플을 채우지 않는다.
**확인**: Agent 첫 단계의 결과 건수와 두 번째 단계 `reportJson.rows`를 비교한다. 둘 다 0건이면
HTML 생성 경로는 정상이다.
**해결**: 같은 사용자와 질문으로 데이터 조회 Tool만 별도 호출해 권한 범위와 조회 결과를
확인한다. HTML 템플릿, renderer 함수 또는 Agent 순차 실행 규칙은 변경하지 않는다.
**재발 방지**: HTML Tool 검증에는 별도의 합성 payload를 사용하고, 데이터 조회 검증과 판정을
분리한다.