abap-adt-mcp
ABAP ADT MCP Server 设计文档
基于 ABAP ADT(ABAP Development Tools)REST API 构建的 Model Context Protocol (MCP) 服务器。 让 AI Agent(Claude Desktop / Cline / VS Code Copilot Agent / 任意 MCP 客户端)无需 SAP GUI 即可 读取、检查、编写、激活、传输 ABAP 代码,覆盖完整开发生命周期。
1. 设计定位与决策
社区已有成熟实现,本项目的定位不是重复造轮子,而是采用"AI 友好"的高层工具设计:
现有项目 | 定位 | 本项目的取舍 |
| 对 | ✅ 复用其底层库 |
| 偏向只读的高层工具(GetProgram/GetClass…) | ✅ 借鉴其"高层工具"思路 |
| AI 友好工作流、安全优先、审计日志 | ✅ 主要设计蓝本:把 lock 编排、错误翻译、只读/白名单封装进工具内部 |
结论:底层通信直接建立在业界验证的 abap-adt-api(v3.1.2)之上,
避免自己重写脆弱的 ADT REST 协议(CSRF、XML 解析、对象元数据)。
在其之上构建一层安全优先、面向 Agent 的高层工具,使 Agent 通过工具名即可理解能力,且不会留下悬空锁、不会误改 SAP 标准对象。
2. 架构分层
┌─────────────────────────────────────────────────────────────┐
│ MCP Client (Claude / Cline / VS Code Agent / FLUJO) │
└───────────────────────────┬─────────────────────────────────┘
│ JSON-RPC over stdio
┌───────────────────────────▼─────────────────────────────────┐
│ MCP Server (this project, TypeScript) │
│ │
│ ├─ tools/read.ts 只读/检查工具 (11) │
│ ├─ tools/write.ts 写入编排工具 (4) — 仅非只读模式注册 │
│ ├─ tools/ddic.ts DDIC 创建工具 (2) — 域 / 数据元素 │
│ ├─ tools/table.ts DDIC 创建工具 (1) — 透明表 │
│ ├─ tools/package.ts DDIC 创建工具 (1) — 包 DEVC/K │
│ ├─ tools/textelements.ts 文本元素读写 (1读+1写) │
│ ├─ tools/functions.ts 函数模块读写 (2读+3写, SE37) │
│ ├─ tools/edit.ts surgical edit 工具 (1) — token 优化 │
│ ├─ tools/decisions.ts 统一 包/请求 决策(澄清 vs 直接绑定) │
│ ├─ resources/ 可浏览资源 (system info / package) │
│ ├─ adt/security.ts 安全层:只读、Z*/Y* 白名单、审计日志 │
│ ├─ adt/errors.ts 错误翻译(可行动的错误信息) │
│ └─ adt/client.ts 连接生命周期 + 每个 ADT 操作的薄封装 │
└───────────────────────────┬─────────────────────────────────┘
│ HTTPS + CSRF (abap-adt-api)
┌───────────────────────────▼─────────────────────────────────┐
│ SAP ABAP System (SICF: /sap/bc/adt 已激活, 用户具 S_DEVELOP) │
└─────────────────────────────────────────────────────────────┘关键原则(对应 MCP Builder 准则)
描述性工具名 —
adt_write_source而非tool1;Agent 靠名字选工具。Zod 类型化参数 — 每个输入都校验,可选参数有默认值。
结构化输出 — 数据返回 JSON,源码类内容返回带行号的文本。
优雅失败 — 所有工具经
safe()包裹,错误转为isError文本,服务器永不崩溃。无状态工具 — 每次调用独立;SAP 会话由单例
ADTClient维护。安全优先 — 见第 4 节。
3. 工具清单(24 个:11 只读 + 13 写入)
只读 / 检查(始终注册,含只读模式)
工具 | 说明 | 关键参数 |
| 连通性 + 会话/角色信息诊断 | — |
| 按名称模式搜索对象(支持 |
|
| 列出包(DEVCLASS)内对象 |
|
| 读取源码,支持行范围切片、行号前缀、截断保护; |
|
| 对象结构大纲;类返回每个 include 的 sourceUri + 方法/属性清单(方法级定位) |
|
| 语法检查(不锁、不写) |
|
| 列出用户传输请求 |
|
| 读 DDIC 表 / CDS 视图数据(WHERE + 行数限制) |
|
| 读程序文本元素(文本符号 / 选择文本 / 列表标题三类),支持语言覆盖 |
|
| 读函数模块(SE37):完整源码 + 解析出的参数接口(IMPORTING/EXPORTING/CHANGING/TABLES/EXCEPTIONS)。不传 |
|
| 列函数组的函数模块/包含程序:返回每个模块名、URI、描述,便于调用/读取前先发现组里有什么 |
|
写入(仅非只读模式注册)
工具 | 说明 | 关键参数 |
| AI 安全编辑编排:冲突检查 → 语法检查 → 锁 → 写 → 解锁(finally) → 可选激活 |
|
| 激活对象(含未激活部分) |
|
| 创建对象壳(无源码),返回 URI 供后续写入 |
|
| 传输请求:create / release / list |
|
| 创建开发包(DEVC/K):包必须嵌套在真实父包下并继承其父包的软件组件+传输层,故必传 |
|
| 创建 DDIC 域(DOMA):骨架 → 锁 → PUT 技术属性(类型/长度/小数) → 解锁 → 可选激活。 |
|
| 创建 DDIC 数据元素(DTEL):引用已有域,技术类型(CHAR/长度/小数)自动从域派生;骨架 → 锁 → |
|
| 创建 DDIC 透明表(TABL):ABAP 字典 DDL 源( |
|
| 写程序文本元素:锁 textelements 资源 → PUT 三类(symbols/selections/headings) → 解锁 → 激活文本池。symbols 用 |
|
| surgical edit(token 优化):Agent 只发 |
|
| 创建函数组(FUGR):在包(默认 |
|
| 创建函数模块(FUGR/FF):在已存在函数组中建模块、写源码、可选激活。源码接受经典 |
|
| 编辑已有函数模块源码(lock → write → unlock → 可选激活)。接受经典或归一化源码,自动归一化。只读模式禁用,名需匹配 Z*/Y* 前缀。首次创建请用 |
|
资源(Resources)
adt://system/info— 连接与角色信息adt://packages/{pkg}— 包内容清单(JSON)
4. 安全模型(最重要)
ABAP 系统里误改 SAP 标准对象或留下悬空锁是真实风险。本设计内建四道防线:
只读模式:
ABAP_MCP_READONLY=true时,13 个写入工具根本不注册(Agent 看不到),其余工具拒绝变更。生产系统强制只读:
ABAP_MCP_SYSTEM_ROLE=prod时,无论READONLY如何,一律只读。Z/Y 前缀白名单**:
assertPrefixAllowed()校验对象名,默认仅允许Z,Y前缀,杜绝误改 SAP 交付对象。审计日志:每次写入追加 JSON 行到
ABAP_MCP_AUDIT_LOG,含时间、工具、对象、传输号、状态。
adt_write_source 的锁编排保证 try/finally 释放锁;即使写入失败也不会留下悬空锁。
4.1 统一「包 / 请求」决策流(所有写入工具共用)
每个写入工具在创建对象前,都要先决定目标开发包与传输请求。逻辑统一由 tools/decisions.ts 的 resolvePackageInput() / resolveTransportInput() 实现,规则如下:
包(package)决策
调用方显式给了
package→ 直接绑定到该包(已大写上送)。调用方没给
package→ 工具返回一条结构化澄清(needsClarification:true、topic:"package"),列出两个动作让 Agent 转问用户:create_package— 调用adt_create_package新建一个真实包(需指定superPackage父包);该选项额外携带requiredInputs:[name, superPackage, description],提示 Agent 必须先从用户处确认这三项必填信息再调建包工具。keep_local— 落到本地包$TMP(无需传输)。
传输请求(transport)决策(同样逻辑)
调用方显式给了
transport→ 直接绑定。包是
$TMP→ 本地对象,不需要传输(resolveTransportInput返回空串,后续调用不传corrNr)。包是非
$TMP且没给transport→ 返回结构化澄清(topic:"transport"、action:create_transport),由 Agent 经adt_manage_transport(create) 建请求后回填。
设计要点:工具绝不静默默认到
$TMP或自动建包——缺参数时一律返回needsClarification,把选择权交还给用户,避免 Agent 在用户不知情的情况下把对象塞进本地包或误建包。澄清对象结构为{ needsClarification, topic, message, options:[{action,label,description,requiredInputs?}], missingFields?, tool, object }:
topic:"package"/"transport"的澄清:options列出可行动作,Agent 转述给用户、回收选择后重新调用同一工具并补上package/transport。
topic:"package_creation"的澄清(由adt_create_package自身返回):当name/superPackage/description任一缺失时返回,missingFields列出缺哪些包必填项,Agent 与用户确认后用完整参数重新调用adt_create_package。这一层保证「创建包前必与用户确认必填信息」,缺项时绝不猜测或崩溃。通用
topic:"missing_inputs"澄清(仅「重型创建」类工具保留):adt_create_function_group(需name/description)、adt_create_domain(需name/description/datatype/length)、adt_create_dataelement(需name/domain/description)、adt_create_table(需name/description/fields)——这类工具自身有多个必填项、且参数结构较复杂,缺项时返回missing_inputs澄清(missingFields列出缺什么 + 中文 label + hint),由decisions.ts的inputClarification()工厂统一生成,缺项检查在 handler 顶部、任何 SAP 调用之前完成。其余 8 个写入工具已还原为 Zod 必填校验(不再返回
missing_inputs澄清):adt_write_source、adt_activate_object、adt_create_object、adt_manage_transport、adt_create_function_module、adt_write_function_module_source、adt_write_textelements、adt_edit_source。它们的必填字段(如objectUri/source/edits/name/action等)在 Zod schema 中声明为必填,缺项时由 MCP SDK 返回标准校验错误,而非结构化澄清。其中adt_manage_transport的action=create需objectUri、action=release需transport仍由 handler 内显式throw校验(create/release 的条件性必填)。
真机验证结论(verify_packages.mjs,VERDICT: PASS):
缺包 → 返回
topic=package澄清;缺传输(非$TMP)→ 返回topic=transport澄清;$TMP路径无需传输。adt_create_package在ZRAP_TEST_192637下建包,自动继承softwareComponent=HOME、transportLayer=ZS4C。adt_create_dataelement/adt_create_domain/adt_create_table在显式transport下创建时,对象均正确绑定到该传输请求(域通过 POST/LOCK/PUT 全程带corrNr实现)。⚠️ 已知 SAP 限制:通过 ADT 创建的包(DEVC/K)因携带 SAP 自动生成的子对象,ADT 删除端点会报「仍然包含开发对象或其它包」而拒绝删除——这是 SAP 行为,非本 server 缺陷;验证脚本对包做尽力删除(不计入功能结论),残留测试包可在 SE21/SE80 手动清理。
5. 错误处理哲学
底层错误(ADT 业务异常 / 网络 / TLS / 锁冲突)被 translateError() 翻译成可让 Agent 自我纠正的中文信息,例如:
证书错误 → 提示设置
SAP_SSL_REJECT_UNAUTHORIZED=false401/403 → 提示检查账号/客户端/是否被锁
锁冲突 → 提示去 SM12 释放或重试
语法错误 → 在
adt_write_source中写前拦截并返回行号,不真正写入
6. 安装与配置
前置条件
Node.js ≥ 20(本项目用 22 验证)
一个启用了 ADT 的 SAP ABAP 系统(
SICF中/sap/bc/adt已激活,用户具S_DEVELOP)
步骤
git clone <this-repo> abap-adt-mcp && cd abap-adt-mcp
npm install
cp .env.example .env # 填入 SAP_URL / SAP_USER / SAP_PASSWORD / SAP_CLIENT
npm run build # 产出 dist/配置文件名:服务器按以下优先级加载(后者不覆盖前者已设的值):
MCP 客户端
env块中直接传入的变量(最高优先级)项目根目录的
.env项目根目录的
MCP.env(本项目的约定文件名,便于与客户端配置共存)所以把配置放在项目根的
MCP.env即可被自动加载,无需改名。
.env / MCP.env 关键项:
SAP_URL=https://s4h.example.com:44300
SAP_USER=DEVELOPER
SAP_PASSWORD=*****
SAP_CLIENT=100
SAP_SSL_REJECT_UNAUTHORIZED=true # 自签名证书开发环境设为 false
ABAP_MCP_SYSTEM_ROLE=dev # prod 会强制只读
ABAP_MCP_READONLY=false
ABAP_MCP_ALLOWED_PREFIXES=Z,Y
ABAP_MCP_AUDIT_LOG=./abap-mcp-audit.log接入 MCP 客户端(任选其一)
a) 任意客户端(npx,无需构建)
{
"mcpServers": {
"abap-adt-mcp": {
"command": "npx",
"args": ["-y", "abap-adt-mcp"],
"env": {
"SAP_URL": "https://...:44300",
"SAP_USER": "DEVELOPER",
"SAP_PASSWORD": "*****",
"SAP_CLIENT": "100"
}
}
}
}b) 本地构建后(stdio)
{
"mcpServers": {
"abap-adt-mcp": {
"command": "node",
"args": ["/绝对路径/abap-adt-mcp/dist/index.js"],
"env": { "SAP_URL": "...", "SAP_USER": "...", "SAP_PASSWORD": "...", "SAP_CLIENT": "100" }
}
}
}调试可用:
npm run inspect启动官方 MCP Inspector。
7. 端到端使用示例
你: 读取 ZCL_INVOICE 的源码并加一个方法,然后激活。
Agent 内部流程:
1. adt_search_objects query="ZCL_INVOICE" → 拿到 objectUri
2. adt_read_source objectUri=... → 当前源码
3. adt_syntax_check source=<新源码> → 预校验(写前拦截错误)
4. adt_write_source objectUri=... source=<新> activate=true
内部: 解析传输 → 语法检查 → lock → setObjectSource → unlock(finally) → activate
5. 返回 { written:true, transport:"NPLK900123", activated:true, success:true }8. AI Token 优化
三类策略降低 Agent 与 SAP 交互时的 token 开销(对标 VSP 的方法级手术 + 上下文压缩):
8.1 Surgical edit(写入侧,adt_edit_source)
Agent 只发 {oldString, newString} 改动片段,不发整个对象源码。工具内部读当前源码 → 逐个替换(要求 oldString 唯一匹配,否则报错并提示加上下文消歧)→ 走 lock/write/unlock/activate 编排。对 2000 行类改一个 5 行方法,请求载荷省 90%+。
8.2 方法级读取定位(读取侧,adt_get_object_structure 增强)
对类返回每个 include(definitions/implementations/main/testclasses)的 sourceUri + 方法/属性清单(name + visibility)。Agent 拿到 include sourceUri 后用 adt_read_source 只读单个 include(如只读实现部分),配合行范围切片定位到具体方法,避免读整个类。
8.3 源码压缩 prologue(读取侧,adt_read_source 的 attachContext)
attachContext=true 时,解析源码中 TYPE/LIKE 引用的 DDIC 对象名(排除 ABAP 内置类型),逐个查 domain/data element 端点取一行摘要(NAME DATATYPE(LENGTH)),附加为 prologue。上限 12 个对象,避免过多调用。Agent 一次读源码即可获得所有引用类型的概要,省去逐个查 DDIC 的往返。
9. 扩展点
ATC 质量门禁:调用
abap-adt-api的 ATC 接口,在adt_write_source中加一道run_atc_check。单元测试:
runUnitTest(url)接入adt_activate_object之后的 CI 流。BTP OAuth2 + PKCE:
abap-adt-api支持传入BearerFetcher,可替换账号密码登录(参考@abapify/adt-cli的 service-key 流程)。Streamable HTTP:用 FLUJO 或
mcp-proxy把 stdio 服务暴露为 HTTP,供远程 Agent 调用。Git/abapGit:
abap-adt-api已支持gitRepos / stageRepo / pushRepo,可加adt_git_*工具。
10. 目录结构
abap-adt-mcp/
├─ package.json
├─ tsconfig.json
├─ .env.example
├─ README.md
└─ src/
├─ index.ts # 入口:建 McpServer、注册、连接 stdio
├─ config.ts # 环境变量 + 安全默认值
├─ adt/
│ ├─ client.ts # ADTClient 单例 + 每操作封装
│ ├─ errors.ts # 错误翻译
│ └─ security.ts # 只读/白名单/审计
├─ tools/
│ ├─ index.ts # 聚合注册(按只读模式决定写入工具)
│ ├─ read.ts # 11 个只读工具(含 attachContext prologue + 方法级定位增强)
│ ├─ write.ts # 4 个写入工具(含 lock 编排)
│ ├─ ddic.ts # 2 个 DDIC 创建工具(域 / 数据元素)
│ ├─ table.ts # 1 个 DDIC 创建工具(透明表)
│ ├─ package.ts # 1 个 DDIC 创建工具(包 DEVC/K,含父包属性继承)
│ ├─ textelements.ts # 文本元素读写(adt_read_textelements + adt_write_textelements)
│ ├─ edit.ts # surgical edit 工具(adt_edit_source, token 优化)
│ ├─ decisions.ts # 统一 包/请求 决策(resolvePackageInput / resolveTransportInput + 澄清结构)
│ ├─ util.ts # safe() / textResult / jsonResult 公共工具
│ └─ util.ts # 结果格式化 + safe() 包裹
└─ resources/
└─ index.ts # system info / package 资源10. 验证状态
✅
npm run build通过(TypeScript 严格模式,零错误)✅ stdio 冒烟测试通过:MCP
initialize→tools/list返回 24 个工具(11 只读 + 13 写入);adt_ping在未配置 SAP 时返回isError而非崩溃✅ 真实 S/4HANA 联调:
adt_create_dataelement创建引用YTEST_192637的数据元素成功并激活(datatype=CHAR, length=11);存在性守卫与坏域守卫均优雅报错✅ 真实 S/4HANA 联调(函数模块):
verify_functions.mjs全绿(VERDICT: PASS)——读BAPI_USER_GET_DETAIL(44 参数)、列ZAIRFC_TOOLS(5 模块)、建组ZFMCPV2_192637并激活、建模块ZFMCPV2_FM(经典含"*注释块源码被自动归一化并激活)、读回解析出IV_NAME/EV_GREETING、编辑加CHANGING CV_COUNTER并激活、清理删除成功。关键结论:ADT 拒绝经典FUNCTION z_fm.尾点形式与"*Local Interface 注释块,本 server 的normalizeFmSource()已将其转换/重写为 ADT 接受的function name+ 小写接口块 + 独立.+endfunction.形式✅ 真实 S/4HANA 联调(统一包/请求决策流):
verify_packages.mjs功能全绿(FUNCTIONAL VERDICT: PASS)——24 工具齐全且含adt_create_package;缺包→返回topic=package澄清、缺传输(非$TMP)→返回topic=transport澄清、$TMP路径无需传输;adt_create_package缺必填项时返回topic=package_creation澄清并列出missingFields(name/superPackage/description),创建包前必与用户确认必填信息;adt_create_package在ZRAP_TEST_192637下建包继承HOME/ZS4C;adt_create_dataelement/adt_create_domain/adt_create_table在显式transport下均正确绑定该请求。清理阶段子对象可正常删除,包本身因 SAP「自动子对象」限制无法经 ADT 删除(已知限制,不计入功能结论)
11. 部署到其他电脑(便携分发)
本服务器源码可移植:config.ts 用 fileURLToPath(import.meta.url) 解析项目根绝对路径读取凭证,不依赖启动时的 cwd,因此放到任意目录、任意电脑都能自定位。跨机器只需处理三件事:依赖、路径、凭证。
一键打包脚本 deploy.bat(推荐,零手敲命令)
仓库根目录已提供 deploy.bat,双击或命令行运行即可自动完成「重建 dist → 打包为 abap-adt-mcp-dist.zip」,且自动排除凭证与日志(MCP.env / .env / *.log / *.err / *.mjs),保留 node_modules/ + dist/,对方免装免构建。
deploy.bat :: 在本机项目根目录运行;产物 abap-adt-mcp-dist.zip 自动生成实现要点:用 %~dp0 自定位项目根(不硬编码路径),robocopy /XF 排除敏感文件后 Compress-Archive 压缩,兼容老版本 PowerShell(不依赖 -Exclude 参数)。对方拿到 zip 后从下方「方式 A 第 2 步」继续即可。
方式 A:零安装便携包(推荐给同事,最直接)
打包:在本机把整个项目目录压缩,排除
MCP.env和*.log(含真实密码,切勿外传)。已带的node_modules/(≈54M) 和dist/让对方免安装、免构建。# Git Bash / PowerShell zip -r abap-adt-mcp.zip abap-adt-mcp -x "abap-adt-mcp/MCP.env" -x "*.log"对方解压到任意目录,例如
D:/tools/abap-adt-mcp。填凭证:复制
.env.example为MCP.env,填入对方自己的 SAP 账号:cp .env.example MCP.env # 编辑 MCP.env,填 SAP_URL/SAP_USER/SAP_PASSWORD/SAP_CLIENT加 MCP 配置:在对方机器的
~/.workbuddy/mcp.json(或其他客户端配置)加:{ "mcpServers": { "abap-adt-mcp": { "command": "node", "args": ["D:/tools/abap-adt-mcp/dist/index.js"], "cwd": "D:/tools/abap-adt-mcp", "env": { "ABAP_MCP_READONLY": "false" } } } }command用node(要求对方node在 PATH,Node ≥ 20);若想彻底免装 node,把node.exe也放进包、command指向./node.exe。必须把路径改成对方机器上的实际位置(本项目
mcp.json里硬编码的绝对路径在别的电脑不存在)。凭证不必写进
env——服务器会自动读项目根下的MCP.env。
启用:在 WorkBuddy 连接器管理页右上角「自定义连接器」点新 server 的 Trust。
方式 B:源码分发(目标机自行安装,包体最小)
对方拿到 src/ + package.json + .env.example,然后:
cd abap-adt-mcp
npm install # 拉取依赖
npm run build # 产出 dist/
cp .env.example MCP.env # 填凭证之后同方式 A 第 4–5 步配置并 Trust。
其他 MCP 客户端
配置结构相同,只是文件位置不同:
Claude Desktop:
%APPDATA%/Claude/claude_desktop_config.jsonCursor / VS Code:对应
mcp.jsonnpx 一行版(无需本地构建,但每次拉包):
{ "mcpServers": { "abap-adt-mcp": { "command": "npx", "args": ["-y", "abap-adt-mcp"], "env": { "SAP_URL":"...", "SAP_USER":"...", "SAP_PASSWORD":"...", "SAP_CLIENT":"100" } } } }
安全提醒
永远不要分发
MCP.env:它含明文 SAP 密码。用.env.example作模板,让每台机器填自己的凭证。生产系统把
ABAP_MCP_SYSTEM_ROLE=prod(强制只读)或ABAP_MCP_READONLY=true,写工具会被隐藏且拒绝变更。
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/abap0917/abap-adt-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server