# ZAA_ICF MCP 서버 (abap-sap) SAP `ZCL_ZAA_AGENT_API` 의 16개 메서드를 LLM 에 붙이는 MCP 서버. 전부 조회만 한다. 브라우저에서 같은 API 를 호출해 보는 웹 테스터와 Bruno 연동은 형제 프로젝트 **`../abap-api-tester`** 에 있다. API 정의(`catalog.py`)와 SAP 호출 클라이언트(`sap_client.py`)는 이 폴더가 원본이고, 테스터가 여기서 가져다 쓴다. ## 실행 ``` cp .env.example .env # SAP_USER / SAP_PASS 채우기 pip install -r requirements.txt # mcp>=2.1 (Python 3.10+) python mcp_server.py # stdio (Claude Code / Claude Desktop) python mcp_server.py --http 8766 # streamable-http http://127.0.0.1:8766/mcp python tools/test_mcp.py [--shrink] # 툴 목록 + 실제 호출 점검 ``` 접속 정보는 `.env` 의 `SAP_URL` / `SAP_USER` / `SAP_PASS`. `.env` 는 스크립트 위치 기준으로 읽으므로 작업 디렉터리와 무관하다. 로그는 stderr 로만 나간다 (stdout 은 프로토콜 채널이라 print 금지). ## 파일 | 파일 | 역할 | |---|---| | `mcp_server.py` | MCP 서버. `catalog.py` 의 16개 메서드를 툴로 노출 (stdio / streamable-http) | | `catalog.py` | 메서드 설명, 파라미터, 샘플, 필드 사전, 코드값 맵, 셀 링크. **테스터와 공유 (원본)** | | `sap_client.py` | SAP 호출·응답 정규화(BOM/utf-16 선언/제어문자 제거, asXML→JSON). **테스터와 공유 (원본)** | | `.mcp.json` | Claude Code 프로젝트 MCP 설정 (이 폴더를 열면 `abap-sap` 서버가 등록됨) | | `tools/test_mcp.py` | MCP 서버 점검 (툴 목록 + 실제 호출 + 잘라내기) | 카탈로그를 고치면 툴 정의도, 테스터 화면도, Bruno 컬렉션(테스터가 생성)도 같이 바뀐다. ## 연결 - **Claude Code**: 이 폴더에 `.mcp.json` 이 있어 프로젝트를 열면 `abap-sap` 서버가 등록된다 (처음 한 번 승인). 다른 폴더에서도 쓰려면 사용자 범위로 등록한다. ``` claude mcp add --transport stdio --scope user abap-sap -- python C:\Users\\EdgeCenter\samsung\abap-mcp\mcp_server.py ``` - **Claude Desktop**: `%APPDATA%\Claude\claude_desktop_config.json` ```json {"mcpServers": {"abap-sap": {"type": "stdio", "command": "python", "args": ["C:\Users\\EdgeCenter\samsung\abap-mcp\mcp_server.py"]}}} ``` `.env` 대신 환경변수로 넘기려면 `"env": {"SAP_USER": "...", "SAP_PASS": "..."}`. ## 툴 툴 정의(이름·설명·파라미터·예시)는 `catalog.py` 에서 **자동 생성**된다. | 툴 | 비고 | |---|---| | `get_package_list` … `get_tcode_info`, `get_object_type` (17개, 메서드명 소문자) | 파라미터는 `IV_` 를 뗀 소문자 (`IV_MAX_ROWS` → `max_rows`). 플래그는 boolean, 날짜는 `YYYY-MM-DD` | | `explain_fields` | 응답 필드의 한국어 뜻과 코드값 표 (`catalog.py` 의 FIELDS / VALUE_MAPS) | | `sap_connection_info` | 접속 대상·호출 규약 확인 (비밀번호 제외) | 응답은 `{"return": {"type", "message", "total_rows"}, …결과, "_meta": {sap_method, sap_params, ms, raw_bytes}}`. `RETURN.TYPE=E` 도 툴 오류가 아니라 정상 응답으로 돌려준다 (LLM 이 메시지를 읽고 판단한다). 네트워크·인증·XML 파싱 실패만 툴 오류. 서버 `instructions` 에 탐색 흐름(패키지 → 오브젝트 → 소스, T코드 → 프로그램, 용어 → 필드)과 응답 읽는 법이 들어 있다. ### LLM 컨텍스트 보호 | 동작 | 기본값 | 환경변수 | |---|---|---| | 목록 툴에서 `max_rows` 생략 시 상한. 잘리면 `return.type=W` + `total_rows`. `0` 이면 전건(SAP 안전 상한 50,000) | 200 | `ABAP_MCP_DEFAULT_MAX_ROWS` | | 응답 JSON 최대 문자 수. 넘치면 가장 큰 소스/목록부터 절반씩 줄이고 `_truncated` 에 무엇을 얼마나 잘랐는지 적는다 | 120,000 | `ABAP_MCP_MAX_OUTPUT_CHARS` | | SAP 호출 타임아웃(초) | 180 | `ABAP_MCP_TIMEOUT` | 소스를 주는 툴(`get_program_source`, `get_version_source`, `get_function_detail`)에는 `line_from` / `line_to` 가 추가돼 있어 긴 소스를 나눠 읽는다. 응답에 `SOURCE_CODE_LINES`(전체 줄 수)와 `SOURCE_CODE_RANGE` 가 붙는다. Include/화면 안의 소스도 같은 범위로 잘린다. 실측(2026-09-07): `SAPMV45A` + Include(원문 4.2MB, include 1,822개)를 상한 15,000자로 부르면 14,378자 + 안내 3줄로 돌아온다. ## 호출 규약 (실측) - `POST /sap/bc/ZAA_ICF/{METHOD}`, `Content-Type: application/json`, 바디 `{"INPUT": {"IV_...": ...}}` - 응답 asXML. XML 선언은 `utf-16` 이지만 실제 UTF-8 → 선언 제거 후 파싱 - 소스코드 안 제어문자(0x0C)가 XML 을 깨뜨림 → 파싱 전 제거 - 날짜는 `YYYY-MM-DD` - 목록 API 의 `IV_MAX_ROWS` 는 생략하면 전건(안전 상한 50,000). 지정해서 잘리면 `RETURN.TYPE=W`, 메시지에 `(전체 M건)`, `RETURN.TOTAL_ROWS=M` (2026-09-04 A안. 그 전엔 기본 200/최대 2,000) ## 서버측 결함 이력 (2026-09-04 기준 모두 해결) - ~~`GET_VERSION_SOURCE` 과거 버전 → HTTP 500~~ 2026-09-04 해결. `SVRS_GET_REPS_FROM_OBJECT` 로 교체 (REPS 만 지원) - ~~`GET_FUNCTION_DETAIL` 의 `FUNC_SOURCE` 항상 빈값~~ 2026-09-04 해결. 함수 Include 를 통째로 읽도록 교체 (출처가 `RETURN.MESSAGE` 에 찍힘) - ~~`GET_PROGRAM_SOURCE` include 목록에 클래스 include 노이즈 혼입~~ 2026-09-04 해결. `=` 포함 또는 30자 초과 이름 제거 - ~~`GET_OBJECT_LIST_BY_PACKAGE` 상한에서 잘려도 `TYPE=S`~~ 2026-09-04 해결. n+1 건을 읽어 초과 시 W (실측 ZFI01 PROG 1,132건) - ~~`GET_FIELD_LIST_BY_TEXT` USED_TABLES 20건 상한이 조용히 잘림 / 후보 0건이면 응답 본문 0바이트~~ 2026-09-04 해결. W + 잘린 엘리먼트 이름, 0건은 빈 RESULT XML - ~~목록 API 기본 상한 200 / 최대 2,000~~ 2026-09-04 A안으로 제거. 생략 시 전건(안전 상한 50,000), `RETURN.TOTAL_ROWS` 추가. 실측: 테이블 `*` 전건 822,899건 → 50,000건 W, 8.7MB, 8초 - ~~`GET_FIELD_LIST_BY_TEXT` USED_TABLES 엘리먼트당 20건 고정 상한~~ 2026-09-04 제거. 후보 500개씩 청크 조회, 사용 테이블 총량 50,000 넘으면 중단 + W. 실측 '회사코드' 전건 499후보/6,949건 0.8MB, '코드' 14,000후보 → 안전 상한 W 10MB/4초 수정 소스는 `~/Downloads/CLASS ZCL_ZAA_AGENT_API DEFINITION.txt` (전체) 와 `METHOD *.txt`. API 명세서(메서드당 1시트)는 `~/Downloads/ABAP_API_명세서_ZAA_ICF_v1.4_2026-09-04.xlsx` (생성기는 `../abap-api-tester/tools/spec/`). ## 응답 인코딩 (2026-09-04 확인) 응답 HTTP 헤더는 `Content-Type: text/html; charset=utf-8` 이고 본문은 UTF-8 BOM 으로 시작한다. XML 선언만 `encoding="utf-16"` 으로 잘못 찍혀 있다. Bruno 는 헤더 charset 으로 디코딩하므로 Bruno 단계에서는 문제없고, 직접 파싱하는 클라이언트만 BOM 과 XML 선언을 떼고 UTF-8 로 읽으면 된다.