SAP-MCP
SAP-MCP
SAP on-premise용 MCP 서버를 ADT를 통해 제공합니다: MCP 엔드포인트와 웹 관리자를 동시에 실행하는 단일 프로세스로, 여러 SAP 시스템에 동시에 연결할 수 있고 디버거와 런타임 진단 도구를 갖추고 있습니다. 도구 이름은 vibing-steampunk의 PascalCase 규칙을 따르며, 다중 시스템 모델과 관리 페이지가 추가되어 있습니다.
SAP에 ABAP 객체를 설치할 필요 없이 사용할 수 있습니다 — 유일한 예외는 RunReport이며, 서버가 자동으로 설치해 줍니다 (그룹 D 항목 참조).
설치
Windows — install.bat을 더블클릭하거나 터미널에서 실행:
install.batPython 3.10+를 확인하고, .venv를 생성하고, 의존성을 설치하고, 샘플 파일에서 systems.json을 생성합니다. 그런 다음 systems.json을 열어 실제 SAP 시스템의 URL / 사용자 / 비밀번호를 입력하고 run.bat을 실행합니다.
기타 플랫폼:
python -m venv .venv && . .venv/bin/activate
pip install -e .
cp systems.example.json systems.json # sửa URL, user, password
python -m sap_mcpsystems.json에는 비밀번호가 포함되어 있으며 이미 .gitignore에 들어 있습니다 — 커밋하지 마세요.
서버 실행
Windows — run.bat을 더블클릭하거나:
run.bat :: cổng 8765, chế độ focused (50 tool)
run.bat 8766 :: đổi cổng
run.bat 8766 expert :: đổi cổng + bật đủ 66 toolrun.bat은 콘솔을 자동으로 UTF-8로 설정하고(로그에 베트남어가 포함되어 있고, cp1252 콘솔에서는 Python이 죽습니다), systems.json이 없으면 샘플 파일에서 자동으로 생성하며, uvicorn이 원시 소켓 오류를 던지게 두는 대신 어떤 PID가 포트를 점유 중인지 명확히 알려줍니다.
http://127.0.0.1:8765을 열어 시스템을 추가/수정/테스트하세요. MCP 엔드포인트는 /mcp에 있습니다.
MCP 클라이언트 연결
프로젝트 디렉터리에 mcp.example.json을 .mcp.json으로 복사하거나, mcpServers 부분을 클라이언트의 기존 구성 파일에 병합하세요:
{
"mcpServers": {
"sap-mcp": {
"type": "http",
"url": "http://127.0.0.1:8765/mcp"
}
}
}다른 포트로 run.bat을 실행하는 경우 포트를 맞게 수정하세요. 클라이언트가 연결하기 전에 서버가 실행 중이어야 합니다 — 이는 streamable-http 전송 방식이므로 클라이언트가 stdio 방식처럼 프로세스를 자동으로 시작하지 않습니다.
시스템 구성 (systems.json)
필드 | 기본값 | 의미 |
| — | SAP 시스템의 |
|
|
|
|
|
|
| — | 기본 인증 |
|
| 자체 서명 인증서의 경우 |
| — | 자체 CA 경로 ( |
|
| 일반 HTTP 상한, 초 |
|
| 활성화해야 쓰기 가능 |
|
| 쓰기가 허용된 패키지 |
| — | 객체 이름별 추가 제한 |
|
| transportable 패키지는 TR 필수 |
|
| 활성화해야 그룹 D 사용 가능 (디버거 + 코드 실행) |
|
| 브레이크포인트에서 대기 가능 시간, 초 |
|
|
|
표준 SAP 네임스페이스의 객체는 항상 거부되며, 비활성화할 수 없습니다.
debug_timeout은 그냥 편의를 위한 숫자가 아닙니다. 브레이크포인트에서 멈춘 코드는 이를 실행한 HTTP 요청을 계속 붙잡고 있으므로, 일반 timeout(30초)이 변수를 보고 있는 동안 요청을 끊어버립니다: 백그라운드 스레드가 죽고 리포트 결과가 완전히 사라지며, DebuggerDetach는 데이터 대신 The read operation timed out을 반환합니다. 이 상한은 디버깅 중에만 늘어납니다(리스너가 있거나 디버기에서 멈춰 있을 때) — 모든 실행에 대해 늘리면 교수형 리포트가 아무도 보지 않는 동안 SAP 워크 프로세스를 30분 동안 점유하게 됩니다.
환경 변수
변수 | 기본값 | 의미 |
|
| 구성 파일 경로 |
|
| HTTP 포트 |
|
|
|
| — | 기능 영역 비활성화, 예: |
그룹 코드: C transport request, D 디버거, P 런타임 진단(덤프, 트레이스) — 아래 도구 섹션 참조. 핵심 도구는 어떤 그룹에도 속하지 않으며 항상 활성화됩니다. 둘 다 비활성화하면(SAP_MCP_DISABLED_GROUPS=D,P) focused/expert가 30/45개 도구로 돌아갑니다.
도구
관리 ListSystems GetConnectionInfo GetSystemInfo
읽기 GetSource GetObjectStructure GetClassInfo GetPackage
GetFunctionGroup SyntaxCheck
· expert: GetProgram GetClass GetInterface GetInclude GetFunction
GetClassInclude
GetSource는 전체 객체 대신 한 구간을 읽을 수 있습니다: around="SELECT"는 주석 밖 첫 번째 출현 주변의 창을 가져오고, from_line/to_line은 정확한 줄 범위를 가져옵니다. 모든 구간은 그것이 구간임을 명시하는 주석 줄로 시작합니다 — UpdateSource에는 전체 버전만 사용할 수 있으며, 창으로 덮어쓰면 나머지가 삭제됩니다. GetPackage에는 max_objects 상한이 있으며 잘렸을 때 명시적으로 알려줍니다.
검색 SearchObject GrepObjects GrepPackages
· expert: GrepObject GrepPackage
데이터 RunQuery GetTableContents
RunQuery는 Open SQL SELECT를 실행하고 결과 테이블을 반환합니다; GetTableContents는 SELECT 문을 대신 구성해 줍니다. allow_write가 필요 없는 이유는 SAP 자체가 이 엔드포인트에서 쓰기 명령을 거부하기 때문입니다:
DELETE FROM t001 … → 400 Invalid query string. Only SELECT statement is allowed.읽기에 대한 장벽은 systems.json의 SAP 사용자 권한입니다 — 해당 사용자가 읽을 수 있는 모든 테이블은 에이전트도 읽을 수 있으며, 인사 테이블도 포함됩니다. 만능 사용자를 구성하지 마세요.
소스 코드 탐색 FindDefinition FindReferences
FindDefinition(system, 'CLAS', 'ZCL_X', symbol='cl_salv_bs_runtime_info') — 서버가 소스에서 기호를 자동으로 찾아(주석 무시) 해당 위치에서 해석하고, 유형, 이름, 구성 요소 목록을 반환합니다. 이름이 더 합리적으로 들리더라도 navigation/target를 사용하지 마세요: 해석에 실패하면 입력 URI 자체를 반환하므로, 가짜 성공이 됩니다. 사용 가능한 경로는 abapsource/codecompletion/elementinfo이며, 본문에 전체 소스가 필요합니다.
FindReferences는 where-used를 반환합니다. SAP는 세 가지 유형의 노드가 섞인 트리를 반환하며, gradeDirect가 있는 항목만 실제 사용처입니다. gradeComponent는 조회 중인 객체 자체의 구성 요소입니다 — 이를 세면 아무도 호출하지 않는 클래스가 갑자기 6개의 사용처를 갖게 됩니다.
쓰기 WriteSource EditSource Activate ActivatePackage CreatePackage
GetInactiveObjects LockObject UnlockObject
· expert: CreateObject UpdateSource DeleteObject
종합 CompareSource CloneObject PrettyPrint ImportFromFile
ExportToFile
그룹 C ListTransports · expert: GetTransport CreateTransport
ReleaseTransport DeleteTransport
그룹 D — 디버거 SetBreakpoint DeleteBreakpoint DebuggerListen
DebuggerPoll DebuggerStopListener DebuggerAttach DebuggerDetach
DebuggerGetStack DebuggerGetVariables DebuggerStep RunClass
RunReport RunUnitTests
allow_debug: true가 필요합니다. 사용 순서:
SetBreakpoint— 줄은 실행 가능한 문이어야 하며, 선언이 아니어야 합니다. 줄을 셀 필요 없음:statement="SELECT"로 서버가 자동으로 찾게 하면(주석을 무시하므로 실행 불가능한 줄에 떨어지지 않음) 줄 번호를 보고합니다.DebuggerListen— 즉시 반환, 리스너가 백그라운드에서 실행RunClass/RunReport/RunUnitTests— 코드 실행브레이크포인트가 발동하면, 3단계는 데이터 대신 즉시
브레이크포인트에서 중지됨 …을 반환합니다.DebuggerPoll은 언제든 상태를 알려줍니다.DebuggerAttach→DebuggerGetStack/DebuggerGetVariables/DebuggerStepDebuggerDetach— 디버기 해제; 코드가 계속 실행되고 3단계의 결과가 여기서 반환됩니다 (오래 걸리면DebuggerPoll에서)
브레이크포인트가 발동하지 않으면 3단계는 일반 도구처럼 결과를 직접 반환합니다.
코드를 실행하는 세 도구가 백그라운드에서 실행되는 이유. 코드가 브레이크포인트에서 멈추면 SAP는 이를 실행 중인 HTTP 요청을 계속 붙잡습니다 — 디버기가 해제된 후에만 호출이 반환됩니다. 동기적으로 호출하면 해당 도구 자체가 멈추고 에이전트는 해제를 위해 DebuggerAttach를 호출할 수 없게 됩니다: 스스로를 잠그는 셈입니다. 따라서 이 세 도구는 백그라운드 스레드의 별도 세션에서 실행되며 리스너가 디버기를 포착하는 즉시 응답합니다.
디버깅 중 각 시스템은 세 개의 분리된 HTTP 세션을 사용합니다: 하나는 리스너 + 디버그 세션용(stateful, 수십 초 동안 유지됨), 하나는 코드 실행용(디버기 해제까지 차단될 수 있음), 하나는 브레이크포인트 설정/삭제용. 분리하지 않으면 서로를 차단합니다: 리스너 세션에서 코드를 실행하면 두 long-poll 루프 사이의 틈에만 끼어들 수 있습니다 — 정확히 SAP에 등록된 리스너가 없는 순간이므로 브레이크포인트가 절대 발동하지 않습니다.
Selection-screen이 있는 리포트 디버깅. 외부 브레이크포인트는 대화형 세션을 포착하지 못합니다 — SE38에서 F8을 눌러도 디버거는 아무것도 보지 못합니다(실제 시스템에서 측정됨). 3단계에서 RunClass 대신 RunReport를 사용하세요: 리포트를 외부 세션에서 실행하므로 브레이크포인트가 발동합니다.
RunReport는 ALV 표시를 차단하면서도 데이터는 가져옵니다
(cl_salv_bs_runtime_info), 따라서 ALV로 끝나는 리포트도 중간에 덤프되지 않습니다. PARAMETERS와 SELECT-OPTIONS(매개변수 이름이 S_로 시작) 및 variant를 모두 받습니다.
RunReport는 SAP에 기록하므로 다른 디버거 도구처럼 allow_debug만 필요한 것이 아니라 allow_write와 allow_debug가 모두 필요합니다. 서버가 $TMP에 두 객체를 자동으로 설치하므로 직접 할 일이 없습니다:
ZCL_MCP_RUNNER— 중간 클래스, 일반적이며 절대 수정되지 않습니다.SUBMIT (mv_report) WITH SELECTION-TABLE mt_sel을 실행하므로, 리포트 이름과 전체 selection-screen이 모두 런타임 데이터입니다.ZMCP_RUNNER_ARGS— 주석 줄만 있는 프로그램으로, 실행 전에 매번 다시 기록됩니다. 클래스는 실행 시READ REPORT로 이를 읽습니다.
*@MCP TOKEN 24b8bff8dfb8477b
*@MCP REPORT ZPG_DEMO
*@MCP MAX 100
*@MCP SEL S_BUKRS S I BT
*@MCP LOW 1000
*@MCP HIGH 2000여전히 기록해야 하는 이유: IF_OO_ADT_CLASSRUN~MAIN( out )는 매개변수를 받지 않습니다 — 쿼리 매개변수도, 본문도 없습니다. 객체의 소스는 ADT REST가 열어주는 유일한 매개변수 전달 채널입니다.
가장 중요한 결과는 안전성입니다: 에이전트가 제공한 어떤 것도 더 이상 ABAP 코드가 되지 않습니다. 이전 버전은 필터 값을 ABAP 리터럴에 내장했으므로, 따옴표 하나만 통과하면 SAP 시스템에 임의의 명령을 주입할 수 있었습니다 — 그 부분은 이스케이프해야 안전했습니다. 이제 값은 주석 줄에 있으며 RSPARAMS 테이블을 통해 SAP에 도달하므로, 깨뜨릴 구문이 없습니다. 줄바꿈 문자만 금지되고(가짜 매개변수 줄이 생성됨), 45자를 초과하는 값은 RSPARAMS-LOW가 CHAR45이므로 거부됩니다 — SAP가 조용히 잘라내면 아무도 모르는 사이에 잘못된 필터링이 발생합니다.
각 실행은 토큰을携带하며; 클래스가 해당 토큰을 반환하고 서버가 대조합니다. 매개변수 기록이 손상되어도 계속 실행되면 리포트는 이전 매개변수로 실행되고 결과는 새 요청의 레이블로 표시됩니다 — 토큰이 이러한 조용한 오류를 차단합니다.
vibing-steampunk(ZADT_VSP 플러그인 필요: 인터페이스 1개, 클래스 3개, WebSocket 핸들러)와 비교하면, RunReport는 더 적게 필요하고 SAPC + SICF를 수동으로 구성할 필요가 없습니다:
vsp ( | SAP-MCP ( | |
설치해야 할 ABAP 객체 | 4 | 2 |
SAPC + SICF 구성 | basis 관리자 필요 | 불필요 |
서버가 자동 설치 가능 | 불가 | 가능 |
실행마다 수정되는 클래스 | 없음 | 없음 |
SELECT-OPTIONS | 없음 ( | 있음 |
실행마다 SAP에 기록 | 없음 | 있음 (주석 파일 하나) |
마지막 줄은 관리자 설치가 필요 없다는 대가입니다: vsp는 WebSocket을 통해 매개변수를 전달하므로 시스템에 접촉하지 않지만, RunReport는 ADT REST가 다른 채널을 열지 않기 때문에 소스를 통해 전달합니다. 대신 기록되는 객체는 주석만 있는 파일입니다 — 깨질 구문이 없고, 로직이 있는 클래스는 그대로 유지됩니다.
그룹 P — 런타임 진단 ListDumps GetDump StartTrace ListTraces
GetTrace DeleteTrace GetSQLTraceState · expert: DeleteTraceRequest
Short dump (ST22). ListDumps는 user/error/program/since로 필터링하고,
GetDump는 summary(무슨 일이 발생했는지, 오류 분석, 중단 지점, call
stack), source(사망 지점의 소스 코드), full 또는 meta를 반환합니다.
성능 측정 (SAT/ATRA). StartTrace('ZPG_X', 'report') → 코드 실행 →
ListTraces → GetTrace. GetTrace는 기본적으로 호출별 시간 프로필을
내림차순으로 반환합니다. view='db'는 테이블별 DB 접근을 반환합니다 — 횟수, 버퍼
조회 횟수, 시간. RunReport(..., trace=True)는 전체 시퀀스를 간소화합니다: 해당
리포트로 제한된 측정 요청을 자동으로 설정합니다.
NET µs % GROSS µs LẦN GỌI TỪ VIỆC
3800 51.2 3800 1 CL_HTTP_SERVER_NET=======C DB: Exec Static
368 5.0 4185 1 SAPLHTTP_RUNTIME Call M. …SEND_RESPONSENW 758에서 측정한 세 가지 사항은 vibing-steampunk가 가정한 것과 반대입니다 — 각각은 따랐을 때 도구를 조용히 망가뜨립니다:
vsp가 하는 방식 | NW 758에서 측정된 결과 | |
feed dump의 Accept |
| 406 — |
dump 필터링 |
| SAP가 무시하고 전체 목록 반환 |
ST05 | feed trace로 읽음 | Fiori URL 하나 반환, 레코드 없음 |
따라서 ListDumps는 MCP 서버 측에서 필터링하고, SQL 문은 ST05가 아닌 ABAP
trace의 dbAccesses에서 가져옵니다. GetSQLTraceState는 여전히 켜진 상태로
방치된 trace를 감지하는 데 유용합니다 — 외부에서 보이지 않지만 전체 시스템을
느리게 만듭니다.
StartTrace는 object 이름이 필수입니다. 제한 없는 trace 요청은
그것을 생성한 HTTP 호출 자체를 즉시 포착합니다: 측정 결과는 전부
ICFSERVICE/HTTP_HEADER_REG — ADT 엔진을 측정하는 것이지 코드를 측정하는
것이 아닙니다 — 그런데도 매우 실제처럼 보이는 숫자 표로 출력됩니다. 제한이
있으면 측정은 그다음 실행에 정확히 떨어집니다 (T001, DDFTX, VARID…).
StartTrace, DeleteTrace, DeleteTraceRequest는 allow_debug가 필요합니다:
이들은 시스템 동작을 변경하며, 방치된 요청은 이후 실행을 몰래 측정합니다. 세
개의 읽기 도구(ListDumps, GetDump, GetSQLTraceState)는 아무것도
필요로 하지 않습니다.
상태
핵심 도구 그룹, D 그룹(디버거, RunReport 포함) 및 P 그룹(dump + trace)이
완료되었습니다 — focused 모드 50개 / expert 모드 66개 도구, NetWeaver 758에서
실행됨. DDIC/i18n, abapGit 및 ABAP helper 부분은 아직 작업하지 않았습니다.
알려진 제한 사항
1. ImportFromFile / ExportToFile은 경로를 제한하지 않습니다. 이 두
도구는 모델이 제공하는 모든 경로를 허용합니다. import_from_file은
os.path.isfile만 확인하고, export_to_file은 os.path.isdir만 확인합니다 —
allowlist가 없고, workspace 디렉터리로 제한하지 않으며, ..나 절대 경로를
차단하지 않습니다. 따라서 에이전트 — SAP에서 읽은 내용의 영향을 받는 에이전트를
포함하여 — 서버 프로세스가 읽을 수 있는 모든 파일을 읽어 SAP에 넣거나, SAP
소스를 쓸 수 있는 모든 경로에 쓸 수 있습니다. 현재 완화 방법: 서버를
통제하는 머신에서만, 에이전트가 접근하길 원하는 파일 외에는 접근 권한이 없는
계정으로만 실행하세요.
2. REST admin 라우트에 인증이 없습니다. / 및 /api/systems*는
127.0.0.1에 바인딩하는 것만으로 보호됩니다. 해당 포트의 loopback에 도달할
수 있는 모든 것은 시스템 구성을 나열, 추가, 수정, 삭제하고 연결 테스트를
활성화할 수 있습니다. 이 포트를 로컬 머신 밖으로 열지 말고, 공유 호스트에서
실행하지 마세요.
3. 디버거는 메모리의 모든 변수를 읽을 수 있습니다. DebuggerGetVariables는
중단점에서 실제 값을 반환하며, 변수에 있는 민감한 데이터 — 비밀번호, 키,
개인 데이터 — 도 포함합니다. 이것은 디버그의 본질이지 버그가 아닙니다.
또한 RunClass는 임의의 ABAP을 실행합니다. 따라서 allow_debug는 기본적으로
꺼져 있으며 개발 시스템에서만 켜야 합니다.
4. RunReport는 파라미터 파일을 공유합니다. $TMP의
ZMCP_RUNNER_ARGS는 각 실행 전에 덮어써집니다. 하나의 서버 내에서는 실행이
이미 큐에 쌓여 있지만(시스템당 실행 채널 하나), 두 서버 또는 두 사람이 같은
시스템을 사용하면 서로의 파라미터를 덮어씁니다. 출력의 토큰이 이를 감지하여
잘못된 데이터를 반환하는 대신 오류로 변환합니다. vibing-steampunk는 세션마다
전용 WebSocket APC를 사용하여 완전히 회피하며, 대신 SICF를 수동으로
구성해야 합니다.
아키텍처
transport/ (HTTP, auth, CSRF) → adt/ (object type, 데이터 반환) →
tools/ (형식 + MCP 등록). adt/uri.py 테이블은 URI를 구성하는 유일한
소스입니다. D 그룹은 시스템당 AdtSession 세 개를 추가로 사용합니다
(transport/debug_pool.py, 채널별 분할) — listener가 백그라운드에서 실행되고,
디버그 세션은 여러 호출에 걸쳐 지속적인 상태를 유지해야 하며, 실행 중인 코드가
breakpoint에서 차단될 수 있기 때문입니다 — 공유 SessionPool에서 빌릴 수 있는
세션은 없습니다.
도구는 event loop가 아닌 worker thread에서 실행됩니다. FastMCP는 event
loop에서 동기 함수를 직접 호출하므로, 그대로 두면 SAP 호출 하나가 서버 전체를
차단합니다: RunReport가 대기하는 동안 에이전트가 DebuggerPoll을 호출할 수
없고, 서로 다른 두 시스템이 서로를 차단하며, 웹 admin이 멈춥니다.
tools/_registry.py는 등록 전에 모든 도구를 anyio.to_thread.run_sync로
감쌉니다. 시스템별 직렬화는 여전히 유지되며 의도적입니다 — SAP 핸들 잠금이
시스템당 하나의 연결에서만 유효하기 때문에 SessionPool에 있습니다.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Hosted Amazon Seller and Vendor MCP server for Claude, ChatGPT, Cursor, Codex, Gemini, Copilot.
Official Microsoft MCP Server to query Microsoft Entra data using natural language
GibsonAI MCP server: manage your databases with natural language
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/NhatPD-VNEXT/SAP-MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server