SAP-MCP
MCP server for SAP on-premise via ADT, enabling multi-system connection, code navigation, data queries, and debugging through natural language. Includes a web admin and runtime diagnostics.
README
SAP-MCP
MCP server cho SAP on-premise qua ADT: một tiến trình chạy cả MCP endpoint lẫn web admin, nối được nhiều hệ SAP cùng lúc, có debugger và công cụ chẩn đoán runtime. Tên tool theo quy ước PascalCase của vibing-steampunk, cộng thêm mô hình đa hệ thống và trang quản trị.
Không cần cài object ABAP nào lên SAP để dùng — ngoại lệ duy nhất là
RunReport, và server tự cài giúp (xem mục Nhóm D).
Cài đặt
Windows — nhấp đúp install.bat, hoặc chạy trong terminal:
install.bat
Nó kiểm tra Python 3.10+, tạo .venv, cài phụ thuộc, và tạo systems.json từ
file mẫu. Sau đó mở systems.json điền URL / user / password hệ SAP thật rồi
chạy run.bat.
Nền tảng khác:
python -m venv .venv && . .venv/bin/activate
pip install -e .
cp systems.example.json systems.json # sửa URL, user, password
python -m sap_mcp
systems.json chứa mật khẩu và đã nằm trong .gitignore — đừng commit nó.
Chạy server
Windows — nhấp đúp run.bat, hoặc:
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 tool
run.bat tự đặt console về UTF-8 (log có tiếng Việt, console cp1252 sẽ làm
Python chết), tự tạo systems.json từ file mẫu nếu chưa có, và báo rõ PID
nào đang chiếm cổng thay vì để uvicorn ném lỗi socket thô.
Mở http://127.0.0.1:8765 để thêm/sửa/test hệ thống. MCP endpoint ở /mcp.
Nối vào MCP client
Chép mcp.example.json thành .mcp.json ở thư mục dự án, hoặc gộp phần
mcpServers vào file cấu hình sẵn có của client:
{
"mcpServers": {
"sap-mcp": {
"type": "http",
"url": "http://127.0.0.1:8765/mcp"
}
}
}
Sửa cổng cho khớp nếu bạn chạy run.bat với cổng khác. Server phải đang chạy
trước khi client kết nối — đây là transport streamable-http, client không
tự khởi động tiến trình như kiểu stdio.
Cấu hình hệ thống (systems.json)
| Trường | Mặc định | Ý nghĩa |
|---|---|---|
url |
— | https://host:port của hệ SAP |
client |
100 |
sap-client |
language |
EN |
sap-language |
username / password |
— | Basic auth |
verify_ssl |
true |
Đặt false cho cert tự ký |
ca_bundle |
— | Đường dẫn CA riêng (thay cho verify_ssl) |
timeout |
30 |
Trần HTTP thường, giây |
allow_write |
false |
Bật mới ghi được |
write_packages |
["Z*","Y*","$TMP"] |
Package được phép ghi |
write_objects |
— | Giới hạn thêm theo tên object |
require_transport |
true |
Package transportable bắt buộc có TR |
allow_debug |
false |
Bật mới dùng được nhóm D (debugger + chạy code) |
debug_timeout |
1800 |
Được dừng ở breakpoint bao lâu, giây |
debug_listen_seconds |
300 |
DebuggerListen chờ mặc định, giây |
Object trong namespace SAP chuẩn luôn bị từ chối, không cấu hình tắt được.
debug_timeout không phải chỉ là con số cho dễ chịu. Code dừng ở breakpoint
giữ luôn request HTTP đã chạy nó, nên timeout thường (30s) cắt ngang giữa lúc
bạn đang xem biến: luồng chạy nền chết và kết quả report mất trắng, DebuggerDetach
trả về The read operation timed out thay vì dữ liệu. Trần này chỉ được nới khi
đang debug (có listener, hoặc đang dừng ở debuggee) — nới cho mọi lần chạy thì
một report treo sẽ ôm một work process của SAP nửa tiếng mà không ai nhìn.
Biến môi trường
| Biến | Mặc định | Ý nghĩa |
|---|---|---|
SAP_MCP_SYSTEMS |
./systems.json |
Đường dẫn file cấu hình |
SAP_MCP_PORT |
8765 |
Cổng HTTP |
SAP_MCP_MODE |
focused |
focused (50 tool) hoặc expert (66 tool) |
SAP_MCP_DISABLED_GROUPS |
— | Tắt vùng tính năng, ví dụ C hoặc C,D |
Mã nhóm: C transport request, D debugger, P chẩn đoán runtime (dump,
trace) — xem mục Tool bên dưới. Tool lõi không thuộc nhóm nào và luôn được bật.
Tắt cả hai (SAP_MCP_DISABLED_GROUPS=D,P) đưa focused/expert về lại 30/45 tool.
Tool
Quản trị ListSystems GetConnectionInfo GetSystemInfo
Đọc GetSource GetObjectStructure GetClassInfo GetPackage
GetFunctionGroup SyntaxCheck
· expert: GetProgram GetClass GetInterface GetInclude GetFunction
GetClassInclude
GetSource đọc được một đoạn thay vì cả object: around="SELECT" lấy cửa
sổ quanh lần xuất hiện đầu tiên ngoài chú thích, hoặc from_line/to_line lấy
đúng khoảng dòng. Đoạn nào cũng mở đầu bằng một dòng chú thích ghi rõ nó là
đoạn — chỉ bản đầy đủ mới đem đi UpdateSource được, ghi đè bằng một cửa sổ là
xoá phần còn lại. GetPackage có trần max_objects và nói rõ khi đã cắt.
Tìm SearchObject GrepObjects GrepPackages
· expert: GrepObject GrepPackage
Dữ liệu RunQuery GetTableContents
RunQuery chạy Open SQL SELECT và trả bảng kết quả; GetTableContents dựng
câu SELECT giúp bạn. Không cần allow_write vì chính SAP từ chối lệnh ghi ở
endpoint này:
DELETE FROM t001 … → 400 Invalid query string. Only SELECT statement is allowed.
Hàng rào cho việc ĐỌC là quyền của user SAP trong systems.json — mọi bảng user
đó đọc được thì agent cũng đọc được, kể cả bảng nhân sự. Đừng cấu hình một user
vạn năng.
Điều hướng mã nguồn FindDefinition FindReferences
FindDefinition(system, 'CLAS', 'ZCL_X', symbol='cl_salv_bs_runtime_info') —
server tự tìm ký hiệu trong source (bỏ qua chú thích) rồi giải tại đó, trả về
loại, tên và danh sách thành phần. Không dùng navigation/target dù tên nghe
hợp lý hơn: nó trả lại chính uri đầu vào khi không giải được, tức thành
công giả. Đường dùng được là abapsource/codecompletion/elementinfo, và nó đòi
toàn bộ source trong body.
FindReferences trả where-used. SAP trả về một cây trộn ba loại nút; chỉ mục
có gradeDirect mới là chỗ dùng thật. gradeComponent là thành phần của
chính object đang tra — đếm vào thì một class không ai gọi bỗng có 6 chỗ dùng.
Ghi WriteSource EditSource Activate ActivatePackage CreatePackage
GetInactiveObjects LockObject UnlockObject
· expert: CreateObject UpdateSource DeleteObject
Tổng hợp CompareSource CloneObject PrettyPrint ImportFromFile
ExportToFile
Nhóm C ListTransports · expert: GetTransport CreateTransport
ReleaseTransport DeleteTransport
Nhóm D — debugger SetBreakpoint DeleteBreakpoint DebuggerListen
DebuggerPoll DebuggerStopListener DebuggerAttach DebuggerDetach
DebuggerGetStack DebuggerGetVariables DebuggerStep RunClass
RunReport RunUnitTests
Cần allow_debug: true. Trình tự dùng:
SetBreakpoint— dòng phải là câu lệnh thực thi, không phải khai báo. Không cần đếm dòng:statement="SELECT"để server tự tìm (nó bỏ qua chú thích nên không rơi vào dòng không thực thi được) và báo lại số dòng.DebuggerListen— trả về ngay, listener chạy nềnRunClass/RunReport/RunUnitTests— chạy code- Nếu breakpoint nổ, bước 3 trả về ngay
Đã dừng ở breakpoint …(không phải dữ liệu).DebuggerPollcho biết trạng thái bất cứ lúc nào. DebuggerAttach→DebuggerGetStack/DebuggerGetVariables/DebuggerStepDebuggerDetach— thả debuggee; code chạy nốt và kết quả của bước 3 trả về ở đây (hoặc ởDebuggerPollnếu nó chạy lâu)
Không có breakpoint nào nổ thì bước 3 trả thẳng kết quả như một tool thường.
Vì sao ba tool chạy code lại chạy ở nền. Khi code dừng ở breakpoint, SAP
giữ luôn request HTTP đang chạy nó — lời gọi chỉ trả về sau khi debuggee được
thả. Gọi đồng bộ thì chính tool đó treo và agent không bao giờ gọi được
DebuggerAttach để thả nó ra: tự khoá chính mình. Ba tool này vì thế chạy trên
một session riêng ở luồng nền và trả lời ngay khi listener bắt được debuggee.
Mỗi hệ dùng ba session HTTP tách biệt khi debug: một cho listener + phiên debug (stateful, bị giữ hàng chục giây), một để chạy code (có thể bị chặn tới lúc thả debuggee), một để đặt/xoá breakpoint. Không tách thì chúng chặn lẫn nhau: chạy code trên session của listener chỉ chen được vào khe giữa hai vòng long-poll — đúng lúc SAP không có listener nào đăng ký, nên breakpoint không bao giờ nổ.
Debug report có selection-screen. External breakpoint không bắt được
phiên dialog — bấm F8 trong SE38 thì debugger không thấy gì (đã đo trên hệ
thật). Dùng RunReport thay cho RunClass ở bước 3: nó chạy report trong
một phiên external, nên breakpoint mới nổ.
RunReport chặn ALV hiển thị mà vẫn lấy được dữ liệu
(cl_salv_bs_runtime_info), nên report kết thúc bằng ALV không dump giữa
chừng. Nhận cả PARAMETERS lẫn SELECT-OPTIONS (tên tham số bắt đầu bằng
S_) và variant.
RunReport ghi vào SAP, nên nó cần cả allow_write lẫn allow_debug,
không chỉ allow_debug như các tool debugger còn lại. Server tự cài hai object
vào $TMP, bạn không phải làm gì:
ZCL_MCP_RUNNER— class trung gian, tổng quát và không bao giờ bị sửa. NóSUBMIT (mv_report) WITH SELECTION-TABLE mt_sel, tức tên report và toàn bộ selection-screen đều là dữ liệu lúc chạy.ZMCP_RUNNER_ARGS— chương trình chỉ gồm dòng chú thích, bị ghi lại trước mỗi lần chạy. Class đọc nó lúc chạy bằngREAD REPORT.
*@MCP TOKEN 24b8bff8dfb8477b
*@MCP REPORT ZPG_DEMO
*@MCP MAX 100
*@MCP SEL S_BUKRS S I BT
*@MCP LOW 1000
*@MCP HIGH 2000
Vì sao vẫn phải ghi: IF_OO_ADT_CLASSRUN~MAIN( out ) không nhận tham số nào —
không query param, không body. Source của một object là kênh truyền tham số
duy nhất ADT REST mở ra.
Hệ quả quan trọng nhất là an toàn: không có thứ gì do agent cung cấp trở
thành mã ABAP nữa. Bản trước nhúng giá trị lọc vào literal ABAP, nên một dấu
nháy lọt qua là chèn được lệnh tuỳ ý vào hệ SAP — chỗ đó phải escape mới an
toàn. Giờ giá trị nằm trên dòng chú thích và tới SAP qua bảng RSPARAMS, nên
không còn cú pháp nào để phá. Chỉ ký tự xuống dòng bị cấm (nó đẻ ra dòng tham
số giả), và giá trị dài quá 45 ký tự bị từ chối vì RSPARAMS-LOW là CHAR45
— SAP sẽ cắt cụt trong im lặng, tức lọc sai mà không ai biết.
Mỗi lần chạy mang một token; class trả lại token đó và server đối chiếu. Ghi tham số hỏng mà vẫn chạy tiếp thì report chạy bằng tham số cũ rồi kết quả được gắn nhãn của lần hỏi mới — token là thứ chặn kiểu sai lặng lẽ đó.
So với vibing-steampunk (đòi plugin ZADT_VSP: 1 interface, 3 class, WebSocket
handler), RunReport cần ít hơn và không phải cấu hình SAPC + SICF thủ công:
vsp (ZADT_VSP) |
SAP-MCP (RunReport) |
|
|---|---|---|
| Object ABAP phải cài | 4 | 2 |
| Cấu hình SAPC + SICF | cần basis admin | không |
| Server tự cài được | không | có |
| Class có sửa mỗi lần chạy | không | không |
| SELECT-OPTIONS | không (hardcode kind='P') |
có |
| Ghi vào SAP mỗi lần chạy | không | có (một file chú thích) |
Dòng cuối là cái giá của việc không cần admin cài đặt gì: vsp truyền tham số
qua WebSocket nên không đụng vào hệ, RunReport truyền qua source vì ADT REST
không mở kênh nào khác. Đổi lại, object bị ghi là một file chỉ gồm chú thích —
nó không có cú pháp để hỏng, và class chứa logic thì đứng yên.
Nhóm P — chẩn đoán runtime ListDumps GetDump StartTrace ListTraces
GetTrace DeleteTrace GetSQLTraceState · expert: DeleteTraceRequest
Short dump (ST22). ListDumps lọc theo user/error/program/since,
GetDump trả summary (chuyện gì xảy ra, phân tích lỗi, chỗ dừng, call
stack), source (mã nguồn tại chỗ chết), full, hoặc meta.
Đo hiệu năng (SAT/ATRA). StartTrace('ZPG_X', 'report') → chạy code →
ListTraces → GetTrace. GetTrace mặc định trả hồ sơ thời gian theo lời
gọi, sắp giảm dần; view='db' trả truy cập CSDL theo bảng — số lần, số lần
lấy từ buffer, thời gian. RunReport(..., trace=True) làm gọn cả chuỗi: nó tự
đặt yêu cầu đo giới hạn đúng report đó.
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_RESPONSE
Ba điều đã đo trên NW 758, ngược với những gì vibing-steampunk giả định — mỗi cái đều làm hỏng tool một cách im lặng nếu làm theo:
| vsp làm | đo được trên NW 758 | |
|---|---|---|
| Accept của feed dump | application/atom+xml |
406 — phải là …;type=feed |
| Lọc dump | gửi $filter FQL |
SAP bỏ qua, trả nguyên danh sách |
ST05 trace/directory |
đọc như feed trace | trả một URL Fiori, không có bản ghi |
Nên ListDumps lọc ở phía server MCP, và câu SQL lấy từ dbAccesses của ABAP
trace chứ không từ ST05. GetSQLTraceState vẫn hữu ích để phát hiện một trace
bị bỏ quên ở trạng thái bật — nó làm chậm cả hệ mà nhìn từ ngoài không thấy gì.
StartTrace bắt buộc có tên object. Một yêu cầu trace không giới hạn sẽ
tóm ngay chính lời gọi HTTP vừa tạo ra nó: bản đo thu được toàn
ICFSERVICE/HTTP_HEADER_REG — đo bộ máy ADT chứ không đo code của bạn — mà
nhìn vào vẫn ra một bảng số liệu trông rất thật. Có giới hạn thì bản đo rơi
đúng vào lần chạy sau đó (T001, DDFTX, VARID…).
StartTrace, DeleteTrace, DeleteTraceRequest cần allow_debug: chúng đổi
hành vi của hệ, và một yêu cầu bỏ quên sẽ đo trộm một lần chạy về sau. Ba tool
đọc (ListDumps, GetDump, GetSQLTraceState) thì không cần gì.
Trạng thái
Nhóm tool lõi, nhóm D (debugger, gồm cả RunReport) và nhóm P (dump + trace)
hoàn tất — 50 tool ở chế độ focused / 66 ở expert, đã chạy trên NetWeaver 758.
Phần DDIC/i18n, abapGit và ABAP helper chưa làm.
Giới hạn đã biết
1. ImportFromFile / ExportToFile không giới hạn đường dẫn. Hai tool
này nhận bất kỳ path nào mà model đưa vào. import_from_file chỉ kiểm tra
os.path.isfile, export_to_file chỉ kiểm tra os.path.isdir — không có
allowlist, không giới hạn về một thư mục workspace, không chặn .. hay
đường dẫn tuyệt đối. Vì vậy một agent — kể cả agent bị ảnh hưởng bởi nội
dung nó đọc được từ SAP — có thể đọc bất kỳ file nào mà tiến trình server
đọc được rồi đưa vào SAP, hoặc ghi source SAP ra bất kỳ path nào ghi được.
Cách giảm rủi ro hiện tại: chỉ chạy server trên máy bạn kiểm soát, dưới
một account không có quyền truy cập file nào ngoài những gì bạn muốn agent
có.
2. Route REST admin không có xác thực. / và /api/systems* chỉ được
bảo vệ bằng cách bind vào 127.0.0.1. Bất cứ thứ gì tới được loopback ở
cổng đó đều có thể liệt kê, thêm, sửa, xoá cấu hình hệ thống và kích hoạt
test kết nối. Đừng mở cổng này ra ngoài máy cục bộ, và đừng chạy nó trên
một host dùng chung.
3. Debugger đọc được mọi biến trong bộ nhớ. DebuggerGetVariables trả về
giá trị thật tại điểm dừng, gồm cả dữ liệu nhạy cảm đang nằm trong biến —
mật khẩu, khoá, dữ liệu cá nhân. Đây là bản chất của debug, không phải lỗi.
Kèm theo đó RunClass thực thi ABAP tuỳ ý. Vì vậy allow_debug mặc định tắt
và nên chỉ bật trên hệ phát triển.
4. RunReport dùng chung một file tham số. ZMCP_RUNNER_ARGS trong $TMP
bị ghi lại trước mỗi lần chạy. Trong một server thì các lần chạy đã được xếp
hàng sẵn (một kênh thực thi cho mỗi hệ), nhưng hai server hoặc hai người cùng
dùng một hệ vẫn đè tham số của nhau. Token trong output phát hiện được chuyện
đó và biến nó thành lỗi, chứ không để trả về dữ liệu sai. vibing-steampunk né
hẳn bằng một WebSocket APC riêng cho mỗi phiên, đổi lại là phải cấu hình SAPC
- SICF bằng tay.
Kiến trúc
transport/ (HTTP, auth, CSRF) → adt/ (object type, trả dữ liệu) →
tools/ (format + đăng ký MCP). Bảng adt/uri.py là nguồn duy nhất dựng URI.
Nhóm D dùng thêm ba AdtSession mỗi hệ (transport/debug_pool.py, chia theo
kênh) vì listener chạy nền, phiên debug phải giữ trạng thái liên tục qua nhiều
lời gọi, và code đang chạy có thể bị chặn ở breakpoint — không session nào
trong số đó mượn được từ SessionPool dùng chung.
Tool chạy ở worker thread, không trên event loop. FastMCP gọi thẳng hàm
đồng bộ trên event loop, nên nếu để nguyên thì một lời gọi SAP chặn cả server:
agent không gọi nổi DebuggerPoll trong lúc RunReport đang chờ, hai hệ khác
nhau chặn lẫn nhau, và web admin đứng hình. tools/_registry.py bọc mọi tool
bằng anyio.to_thread.run_sync trước khi đăng ký. Việc tuần tự hoá theo từng
hệ vẫn còn nguyên và vẫn là chủ ý — nó nằm ở SessionPool, vì lock handle của
SAP chỉ hợp lệ trên một connection cho mỗi hệ.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.