SAP-MCP

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.

Category
Visit Server

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_writechí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:

  1. 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.
  2. DebuggerListen — trả về ngay, listener chạy nền
  3. RunClass / RunReport / RunUnitTests — chạy code
  4. Nếu breakpoint nổ, bước 3 trả về ngay Đã dừng ở breakpoint … (không phải dữ liệu). DebuggerPoll cho biết trạng thái bất cứ lúc nào.
  5. DebuggerAttachDebuggerGetStack / DebuggerGetVariables / DebuggerStep
  6. DebuggerDetach — thả debuggee; code chạy nốt và kết quả của bước 3 trả về ở đây (hoặc ở DebuggerPoll nế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ằng 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

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-LOWCHAR45 — 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
Class có sửa mỗi lần chạy không không
SELECT-OPTIONS không (hardcode kind='P')
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 → ListTracesGetTrace. 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. //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

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.

Official
Featured
TypeScript
Audiense Insights MCP Server

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.

Official
Featured
Local
TypeScript
Magic Component Platform (MCP)

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.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

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.

Official
Featured
TypeScript
Kagi MCP Server

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.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

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.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured