abap mcp adt学习一

作为传统 ABAP 开发者,阅读 mcp-abap-abap-adt-api(以及底层 abap-adt-api)源码时,如果没先搞清 CSRF Token、stateful / stateless 会话模式等概念,很容易被底层复杂的 HTTP 封装绕晕。本文将通过Postman 实际调用 SAP ADT REST 服务,把这些抽象的底层概念变成"看得见、摸得着"的可观测实例。示例对象为程序 YJW_DEMO01

1.获取x-csrf-token

服务URL:

http://ip:port/sap/bc/adt/compatibility/graph

HTTP方法:get

请求信息:header参数:X-CSRF-Token,值:Fetch

响应结果 :在 Response 的 Headers 中,会返回一个随机生成的 x-csrf-token 字符串

X-CSRF-Token 用来防 CSRF(跨站请求伪造)

它防的是什么

浏览器会自动带上已登录站点的 Cookie。如果只靠 Cookie 鉴权,恶意网页可以偷偷让你的浏览器对 SAP 发 POST(改对象、解锁、删东西),服务器会当成"你本人操作"。

CSRF Token 的思路是:

  1. 服务器先发一个不可预测的 token
  2. 写操作必须由客户端显式放进请求头带回
  3. 恶意第三方页面通常读不到你从 ADT 拿到的 token(同源策略)
  4. 没有正确 token → 拒绝(常见 403)

Cookie 证明"浏览器里有会话";Token 证明"这是知情的客户端主动发起的写请求"。

为什么 ADT 需要用到它

ADT 的 lock / unlock / 改源码都是会改系统状态的写操作。SAP 对这类请求校验

同一会话 Cookie + 匹配的 X-CSRF-Token才允许写操作

Cookie 证明你登录了;CSRF Token 证明这次写操作是你(或你的工具)发起的,不是别人借你的浏览器偷偷发的。

abap-mcp-adt的相关代码

GitHub - marcellourbani/abap-adt-api: Abap Developer Tools client · GitHub

路径:src/AdtHTTP.ts

方法: _request

2.加锁对象

GitHub - mario-andreschak/mcp-abap-abap-adt-api: MCP-Server for SAP ABAP wrapping abap-adt-api · GitHub

代码路径

src/handlers/ObjectLockHandlers.ts

javascript 复制代码
// 1. 在调用底层的 lock 之前,显式将客户端状态修改为 stateful
this.adtclient.stateful = session_types.stateful;

// 2. 然后调用 adt client 的 lock 方法加锁对象
const lockResult = await this.adtclient.lock(args.objectUrl, args.accessMode);

2.1 切换成有状态

在调用底层的 lock 之前,将客户端状态改为了 session_types.stateful

this.adtclient.stateful = session_types.stateful

本质上设置adt rest服务的请求头header的属性X-sap-adt-sessiontype为stateful,参考4.会话状态的说明

2.2 然后调用adt client的lock方法加锁对象

const lockResult = await this.adtclient.lock(args.objectUrl, args.accessMode);

SAP enqueue 成功后发的 会话锁令牌。后续 setObjectSource / unLock 都要带上它,证明持有这次锁。会话断开或 unLock 后就失效。

测试加锁测试程序YJW_DEMO01

服务URL:

http://ip:port/sap/bc/adt/programs/programs/yjw_demo01?_action=LOCK&accessMode=MODIFY

HTTP方法:post

输入步骤1获取到x-csrf-token

设置X-sap-adt-sessiontype成stateful,注意一定要stateful才触发加锁

响应结果 :接口成功后会返回一段 XML,解析后可以拿到一个加密的 lockHandle(会话锁令牌)

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<asx:abap version="1.0" xmlns:asx="http://www.sap.com/abapxml">
    <asx:values>
        <DATA>
            <LOCK_HANDLE>0DDA6DC1A34A39BDA3AEB448769520EFA8736FE5</LOCK_HANDLE>
            <CORRNR/>
            <CORRUSER/>
            <CORRTEXT/>
            <IS_LOCAL>X</IS_LOCAL>
            <IS_LINK_UP/>
            <MODIFICATION_SUPPORT/>
            <SCOPE_MESSAGES/>
        </DATA>
    </asx:values>
</asx:abap>

此时保持 Postman 连通状态,登录 SAP GUI 进入事务码 SM12 查看锁记录,会看到程序 YJW_DEMO01 已经被当前使用的 HTTP 帐号成功锁定了

adt.client.lock的底层代码

路径:

javascript 复制代码
async function lock(h, objectUrl, accessMode = "MODIFY") {
    (0, AdtException_1.ValidateObjectUrl)(objectUrl);
    (0, AdtException_1.ValidateStateful)(h);
    const qs = { _action: "LOCK", accessMode };
    const response = await h.request(objectUrl, {
        headers: {
            Accept: "application/*,application/vnd.sap.as+xml;charset=UTF-8;dataname=com.sap.adt.lock.result"
        },
        method: "POST",
        qs
    });
    const raw = (0, utilities_1.parse)(response.body);
    const locks = (0, utilities_1.xmlArray)(raw, "asx:abap", "asx:values", "DATA");
    return locks[0];
}

3.解锁对象

请求头 (Headers):

同样需要携带有效的 X-CSRF-Token 和 X-sap-adt-sessiontype: stateful。

URL 参数:必须将步骤 2 中获取的 lockHandle 作为 URL 参数传入,证明你持有这次锁的所有权。

HTTP 方法:POST

服务 URL:

http://ip:端口/sap/bc/adt/programs/programs/yjw_demo01?_action=UNLOCK&accessMode=MODIFY&lockHandle=0DDA6DC1A34A39BDA3AEB448769520EFA8736FE5

请求发送成功后,再次进入 SAP 后台刷新 SM12,该程序的锁记录已消失,资源成功释放。

路径

src/handlers/ObjectLockHandlers.ts

4.会话状态

X-sap-adt-sessiontype 属性是 SAP ABAP 核心开发团队在设计 ADT (ABAP Development Tools, 即 Eclipse 及 VS Code/Cursor 插件底层的 REST API) 时使用的内部/私有扩展 HTTP 头部字段。它的核心作用是指导 ABAP 后端的 ICF (Internet Communication Framework) 处理程序该如何维护用户的上下文(Roll Area)。

Stateless(无状态,默认)

每个 HTTP 请求到达后端后,系统会分配一个独立的内存上下文(Roll Area),请求处理完毕后立即释放。

Stateful(有状态)

当客户端通过 X-sap-adt-sessiontype: stateful 发送请求时,ABAP 后端会将其绑定到一个固定的工作进程上下文中。只要会话未超时或未显式关闭,全局变量、锁机制(如排他性编辑锁)都会保留在内存中。

adt客户端设置状态的代码

代码路径

经常在mcp的代码里看到设置状态的代码

this.adtclient.stateful = session_types.stateful;

本质上是设置adt rest服务的请求头header的属性X-sap-adt-sessiontype为stateful

const SESSION_HEADER = "X-sap-adt-sessiontype"

5.总结

  1. X-CSRF-Token 是前后端安全通信的门票。

  2. X-sap-adt-sessiontype: stateful 是在无状态的 HTTP 世界里拉起 ABAP 工作进程 Roll Area、维持 ENQUEUE 锁的隐形纽带。

理解了这两点,再去看MCP源码,思路就会豁然开朗。