feat(backend): SAP MCP 를 env 로 켜고 끄게 — 고객사 -12 에 별도 기동 절차

opencode.fabrix.json.tmpl 의 sap-icf 를 MCP_PORT/MCP_API_KEY 로 렌더, 키 없으면 enabled:false.
.env.example 에 SAP_*·MCP_* 블록. README 에 -13 번들 재활용 절차(컨테이너 간 통신 금지라 -12 에 따로).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
lee-hyeon-cheol
2026-09-21 10:43:49 +09:00
co-authored by Claude Fable 5.1
parent 72a06d6c07
commit 8571db5bee
5 changed files with 53 additions and 4 deletions
+12
View File
@@ -35,3 +35,15 @@ AAF_RELAY_STREAM_USAGE=
AAF_GATEWAY_KEY= AAF_GATEWAY_KEY=
# OpenCode 가 붙을 이 서버 포트 (render_opencode.py 가 씀) # OpenCode 가 붙을 이 서버 포트 (render_opencode.py 가 씀)
BACKEND_PORT=8001 BACKEND_PORT=8001
# ── SAP 조회 MCP (고객사 전용, -12 에 따로 띄움. ABAP_OPENCODE mcp.bundle.cjs 재활용) ──
# MCP_API_KEY 가 있으면 render_opencode.py 가 OpenCode 에 sap-icf 를 켬. 비우면 꺼짐(로컬).
MCP_PORT=8888
MCP_API_KEY=
# 아래는 MCP 프로세스가 읽는 SAP 접속값 — ABAP_OPENCODE bare/.env 와 같은 키
SAP_HOST_ICF=
SAP_USERNAME_ICF=
SAP_PASSWORD_ICF=
SAP_CLIENT_ICF=100
SAP_VERIFY_SSL_ICF=false
SAP_API_BASE_ICF=
+25
View File
@@ -100,3 +100,28 @@ curl http://localhost:8001/api/ito/models
- 설정이 비어 있어도 서버는 뜨고, `/api/ito/chat/completions` 만 503 으로 뭐가 빠졌는지 알려줌. - 설정이 비어 있어도 서버는 뜨고, `/api/ito/chat/completions` 만 503 으로 뭐가 빠졌는지 알려줌.
- ABAP_OPENCODE 의 `apps_ito` 를 통째로 안 가져온 이유: 그쪽은 11k 줄 + DB 테이블 + langgraph 등 의존성 15개인데 여기 필요한 건 통과 중계뿐. 사용자별 키(IP 마스터)는 아직 없음 — 서비스 키 하나로 감. - ABAP_OPENCODE 의 `apps_ito` 를 통째로 안 가져온 이유: 그쪽은 11k 줄 + DB 테이블 + langgraph 등 의존성 15개인데 여기 필요한 건 통과 중계뿐. 사용자별 키(IP 마스터)는 아직 없음 — 서비스 키 하나로 감.
- 개발로 되돌리기: `git checkout opencode/opencode.json`. - 개발로 되돌리기: `git checkout opencode/opencode.json`.
## SAP 조회 MCP 붙이기 (고객사, -12 에 따로 띄움)
컨테이너끼리(-12↔-13) 통신은 금지라 -13 의 MCP 를 안 빌리고 -12 에 하나 더 띄움. 코드는 ABAP_OPENCODE 의 번들 한 파일(`mcp.bundle.cjs`) 그대로.
```bash
# PC: -13 에서 번들 꺼내 -12 로 (환경값은 -13 bare/.env 의 SAP_*·MCP_API_KEY 를 손으로 옮김)
scp -P 8916 root@10.196.81.34:/www/abap-ito/web/MCP/code/dist/mcp.bundle.cjs .
scp -P 8912 mcp.bundle.cjs root@10.196.81.34:/www/
# -12
cd /www/ABAP_CODE_ASSISTANT/5_django_backend
vi .env # MCP_PORT=8888, MCP_API_KEY, SAP_HOST_ICF/USERNAME/PASSWORD/CLIENT/VERIFY_SSL/API_BASE 채움 (.env.example 의 SAP 블록)
set -a; . ./.env; set +a
mkdir -p /www/mcp && cp /www/mcp.bundle.cjs /www/mcp/
(cd /www/mcp && nohup node mcp.bundle.cjs > /www/ABAP_CODE_ASSISTANT/5_django_backend/logs/mcp.log 2>&1 &)
.venv/bin/python opencode/render_opencode.py # MCP_API_KEY 있으면 sap-icf enabled:true 로 렌더
pkill -f "opencode-bin serve"
(cd opencode && nohup /www/opencode-bin serve --hostname 127.0.0.1 --port 4096 > ../logs/opencode.log 2>&1 &)
# 확인 — 도구 목록에 sap 도구 6개
curl -s -X POST http://127.0.0.1:8888/mcp -H "Authorization: Bearer $MCP_API_KEY" -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | head -c 400
```
- 로컬 개발에선 `MCP_API_KEY` 를 비워두면 `sap-icf``enabled:false` 로 렌더돼 OpenCode 가 MCP 를 안 찾음.
- MCP 가 zai 모드로 뜨는지는 `logs/mcp.log``mode=zai` 찍히는 걸로. 고객사 SAP 은 zdict 가 아니라 ZAI_ICF 라 `SAP_API_BASE_ICF` 를 -13 값과 같게.
@@ -17,9 +17,9 @@
"mcp": { "mcp": {
"sap-icf": { "sap-icf": {
"type": "remote", "type": "remote",
"url": "{env:SAP_MCP_URL}", "url": "http://127.0.0.1:${MCP_PORT}/mcp",
"headers": { "Authorization": "Bearer {env:SAP_MCP_KEY}" }, "headers": { "Authorization": "Bearer ${MCP_API_KEY}" },
"enabled": false "enabled": ${SAP_MCP_ENABLED}
} }
} }
} }
+5 -1
View File
@@ -34,7 +34,7 @@ def load_env(path: Path) -> dict[str, str]:
k, v = line.split("=", 1) k, v = line.split("=", 1)
v = v.split(" #", 1)[0].strip().strip("\"'") # 줄 끝 주석은 두 칸 띄고 # v = v.split(" #", 1)[0].strip().strip("\"'") # 줄 끝 주석은 두 칸 띄고 #
out[k.strip()] = v out[k.strip()] = v
out.update({k: v for k, v in os.environ.items() if k.startswith(("AAF_", "BACKEND_PORT"))}) out.update({k: v for k, v in os.environ.items() if k.startswith(("AAF_", "BACKEND_PORT", "MCP_"))})
return out return out
@@ -46,6 +46,10 @@ def render(env: dict[str, str]) -> str:
values["AAF_FABRIX_MODELS_JSON"] = json.dumps({mid: {"name": name} for mid, name in models.items()}, ensure_ascii=False) values["AAF_FABRIX_MODELS_JSON"] = json.dumps({mid: {"name": name} for mid, name in models.items()}, ensure_ascii=False)
values["AAF_FABRIX_DEFAULT_MODEL"] = next(iter(models)) values["AAF_FABRIX_DEFAULT_MODEL"] = next(iter(models))
values.setdefault("BACKEND_PORT", "8001") values.setdefault("BACKEND_PORT", "8001")
values.setdefault("MCP_PORT", "8888")
# SAP MCP: MCP_API_KEY 가 있으면 켬. 없으면 끔(로컬 개발)
values["SAP_MCP_ENABLED"] = "true" if values.get("MCP_API_KEY") else "false"
values.setdefault("MCP_API_KEY", "")
values["AAF_GATEWAY_KEY"] = values.get("AAF_GATEWAY_KEY") or "x" # SDK 가 빈 키를 거부해서 더미 values["AAF_GATEWAY_KEY"] = values.get("AAF_GATEWAY_KEY") or "x" # SDK 가 빈 키를 거부해서 더미
missing: list[str] = [] missing: list[str] = []
+8
View File
@@ -0,0 +1,8 @@
# 작업로그 · 2026-09-21 (월)
| 시간 | 내용 |
|------|------|
| 09:12 | DB 저장 범위 정리 — 채팅 텍스트만 서버, 이미지·클립보드·스니펫은 PC. 이미지 재진입 시 사라지는 건 stage-2 남은 것에 기록 |
| 10:26 | 삼성향 라이트 팔레트 추가·기본으로 — 삼성닷컴 실측 토큰(검정 CTA·흰 바탕·#f7f7f7 면·#ddd 테두리), 알약 radius 20px. 다크는 그대로. 폰트는 라이선스 근거 없어 미적용 |
| 10:40 | PwC 로고·마크 전부 제거(LogoMark 빈 렌더, 파비콘 투명), 기본 모드 라이트로(예전 dark 저장값 한 번 초기화), 삼성 팔레트 포인트를 Samsung Blue #1428A0 로(버튼·링·링크) |
| 10:43 | SAP MCP 를 -12 에 따로 띄우는 준비 — OpenCode 템플릿 sap-icf 를 MCP_API_KEY 유무로 on/off 렌더, .env.example SAP 블록, README 절차(-13 번들 재활용, 컨테이너 간 통신 없이) |