1. 当 SAProuter 只放行 RFC,Claude 就被挡在 ADT 门外
如果你在 SAP ABAP 项目现场待过,大概率见过这种网络拓扑:开发机连不进应用服务器的 ICM HTTP/HTTPS 端口,所有流量必须经过 SAProuter,而 saprouttab 里只开了 DIAG、Gateway、RFC 这几条 NI 协议路由。SAP GUI 登录正常,Eclipse ADT 也能打开包、类、CDS View,但一旦换成基于 HTTP 的自动化工具,curl 打 ICM 端口超时,Node.js 的 ADT Client 报连接失败,vsp 这类把 ADT 能力通过 MCP 暴露给 Claude 的 Server 同样连不上。
表面看很矛盾:Eclipse ADT 明明能用,为什么 Claude 用不了?原因在于 Eclipse ADT 在这种受限网络里并没有走普通 HTTP 到 ICM,而是把 ADT 的 HTTP 请求封装进 RFC,经由 SAProuter 允许的 RFC 通道,调用系统里的标准 Function ModuleSADT_REST_RFC_ENDPOINT。这个 FM 属于SAP_BASIS下的BC-DWB-AIE组件,描述是 Endpoint for ADT REST Framework,导入参数REQUEST、导出参数RESPONSE,是 Remote-Enabled Module。也就是说,系统侧本来就存在一条「ADT over RFC」的暗道,只是 HTTP-only 的 AI 工具不知道。
这篇要解决的问题很具体:在 RFC-only 与 SAProuter 隔离的企业内网里,怎么让 Claude 真正参与 SAP ABAP 开发,而不是停在离线生成示例代码的阶段。思路是本地起一个 HTTP 到 RFC 的桥,把 vsp 发出的 ADT HTTP 请求转成 RFC 调用,再通过 TaoToken 统一 Key 把 Claude 侧的模型通道接进来。适合谁:手上有 Eclipse ADT 可用、但 HTTP 到 ICM 被挡的 ABAP 开发者,以及想给团队搭一套可控 AI 结对环境的架构同学。
2. TaoToken 前置:统一 Key 与 API 通道
桥接解决的是「Claude 到 SAP」这一段网络路径,但 Claude 本身要能稳定调用模型,还需要一个统一的模型入口。TaoToken 在这里承担的角色是统一 Key 与 API 通道:你不用在 Cline、CC Switch、Claude Code 之间分别维护不同的模型配置,而是用一套 Key 走同一个 API 入口,把模型对话、编码补全、Agent 调用都收敛到一处。
对 ABAP 场景来说,这一点比普通 Web 开发更重要。SAP 项目现场经常是内网隔离、代理受限、证书链复杂,如果每个工具都要单独配一套模型通道,排障成本会成倍上升。统一入口之后,模型侧出问题只需要查一个地方,网络侧出问题就集中在桥接和 SAProuter 这一段,边界清晰。
接入前先在 TaoToken 控制台创建 API Key,地址是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。创建好之后,模型对话可以在 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 验证,长期编码和 Agent 场景建议看 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,控制台在 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
注意:TaoToken 是模型 API 通道,不替代 SAP 侧的授权体系。桥接层进入 SAP 用的是 RFC 用户,模型侧用的是 API Key,两者职责分开,审计时也更容易追溯。
3. 可复制配置:settings.json 与 config.toml 骨架
这一节给出两个可直接改的配置骨架。第一个是 Cline / Claude Code 这类工具的settings.json,第二个是 CC Switch 或类似多环境切换工具的config.toml。两者都指向 TaoToken 的统一 API 入口,同时把本地桥接端口作为 ADT 访问地址。
先看settings.json。核心是把模型 provider 指向 TaoToken 的 API 地址,把 API Key 用环境变量注入,避免明文写死在仓库里。ADT 侧则通过SAP_URL指向本地桥接端口,让 vsp 以为自己在访问一个普通 ADT HTTP 服务。
{ "modelProvider": { "name": "taotoken", "baseUrl": "https://taotoken.net/api", "apiKeyEnv": "TAOTOKEN_API_KEY", "model": "claude-sonnet" }, "mcpServers": { "sap-adt-rfc": { "command": "C:\\path\\to\\x64\\python.exe", "args": ["C:\\path\\to\\adt-rfc-bridge\\vsp_launch.py"], "env": { "BRIDGE_PORT": "8410", "RFC_ASHOST": "10.0.0.1", "RFC_SYSNR": "00", "RFC_CLIENT": "100", "RFC_USER": "YOUR_RFC_USER", "RFC_PASSWD": "your-rfc-password", "RFC_SAPROUTER": "/H/router.example.com/S/3299", "ADT_CLIENT": "C:\\path\\to\\vsp.exe", "SAP_URL": "http://127.0.0.1:8410", "SAP_USER": "YOUR_RFC_USER", "SAP_PASSWORD": "your-rfc-password", "SAP_CLIENT": "100" } } } }再看config.toml,适合 CC Switch 这类需要多环境切换的场景。每个 SAP Client 分配一个独立端口,DEV 100 用 8410,QAS 200 用 8420,一个桥对应一个 RFC 用户、一个 Client、一个端口,不混用,也不暴露到局域网。
[taotoken] base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" default_model = "claude-sonnet" [bridge.dev100] bridge_port = 8410 rfc_ashost = "10.0.0.1" rfc_sysnr = "00" rfc_client = "100" rfc_user = "YOUR_RFC_USER" rfc_passwd_env = "SAP_DEV100_PASSWD" rfc_saprouter = "/H/router.example.com/S/3299" sap_url = "http://127.0.0.1:8410" [bridge.qas200] bridge_port = 8420 rfc_ashost = "10.0.0.2" rfc_sysnr = "00" rfc_client = "200" rfc_user = "YOUR_RFC_USER" rfc_passwd_env = "SAP_QAS200_PASSWD" rfc_saprouter = "/H/router.example.com/S/3299" sap_url = "http://127.0.0.1:8420"桥接程序的核心逻辑很小,但很精准。它在本机监听 HTTP 地址,拿到请求后组装SADT_REST_REQUEST,通过 PyRFC 调用SADT_REST_RFC_ENDPOINT,再把SADT_REST_RESPONSE转回 HTTP 响应。下面这段是桥接核心的示意,不是完整实现,但能看清数据流。
def adt_call(method, uri, headers, body): # SADT_REST_RFC_ENDPOINT 对 HEAD 支持不理想,转成 GET,HTTP 层再丢弃 body fm_method = "GET" if method == "HEAD" else method req = { "REQUEST_LINE": {"METHOD": fm_method, "URI": uri, "VERSION": "HTTP/1.1"}, "HEADER_FIELDS": [{"NAME": k, "VALUE": v} for (k, v) in headers], "MESSAGE_BODY": body or b"", } with _lock: conn = get_conn() res = conn.call("SADT_REST_RFC_ENDPOINT", REQUEST=req) r = res["RESPONSE"] code = int(r["STATUS_LINE"]["STATUS_CODE"]) out_headers = [(h["NAME"], h["VALUE"]) for h in r["HEADER_FIELDS"]] return code, r["STATUS_LINE"]["REASON_PHRASE"], out_headers, r["MESSAGE_BODY"]PyRFC 连接参数里,SAProuter 用/H/host/S/port格式传入,这是 SAP NW RFC SDK 要求的写法,不能塞进 HTTP proxy 配置。
from pyrfc import Connection conn = Connection( ashost="10.0.0.1", sysnr="00", client="100", user="YOUR_RFC_USER", passwd="your-rfc-password", saprouter="/H/router.example.com/S/3299", )4. 验证请求:从 RFC 连通到 ABAP 补全
配置写完不要直接上 Claude,按层次往上验证,出错时才知道卡在哪一层。第一步验证 PyRFC 能否加载,这一步失败基本是原生库或位数问题,不是 SAP 用户密码错。
python -c "from pyrfc import Connection; print('pyrfc ok')"第二步用STFC_CONNECTION验证 RFC 基本连通。这个 FM 是 SAP 标准测试函数,能返回说明 SAProuter、Gateway、RFC 用户这条链路已经通了。
from pyrfc import Connection conn = Connection( ashost="10.0.0.1", sysnr="00", client="100", user="YOUR_RFC_USER", passwd="your-rfc-password", saprouter="/H/router.example.com/S/3299", ) result = conn.call("STFC_CONNECTION", REQUTEXT="ping") print(result["ECHOTEXT"])第三步启动桥接程序,确认本地端口在监听。
python adt_rfc_bridge.py # 期望输出类似:bridge listening on http://127.0.0.1:8410 -> SAProuter backend第四步做一次 ADT Discovery,验证 HTTP 到 RFC 再到 ADT REST Framework 的链路。能返回application/atomsvc+xml一类响应,说明桥接层工作正常。
curl -i http://127.0.0.1:8410/sap/bc/adt/discovery第五步把 vsp 和 Claude 接上去,让 Claude 读一个真实 ABAP 对象。比如让它搜索某个 Z 包下所有调用特定 Function Module 的 Report,或者读取一个 RAP Behavior Implementation 分析 EML 调用逻辑。如果 Claude 能返回真实对象内容而不是凭空生成,说明整条链路已经打通。
ABAP 侧的重构结果可以用下面这段作为参照,按团队规范 ABAP 代码块用 sql 包裹。Claude 通过 ADT 读到真实代码后,可以建议把查询逻辑抽到类方法里,便于后续单元测试。
REPORT z_demo_product_lookup. PARAMETERS p_matnr TYPE matnr OBLIGATORY. CLASS lcl_product_reader DEFINITION FINAL. PUBLIC SECTION. TYPES BEGIN OF ty_product, matnr TYPE mara-matnr, mtart TYPE mara-mtart, meins TYPE mara-meins, END OF ty_product. CLASS-METHODS read_product IMPORTING iv_matnr TYPE matnr RETURNING VALUE(rs_product) TYPE ty_product RAISING cx_sy_open_sql_db. ENDCLASS. CLASS lcl_product_reader IMPLEMENTATION. METHOD read_product. SELECT SINGLE matnr, mtart, meins FROM mara WHERE matnr = @iv_matnr INTO @rs_product. ENDMETHOD. ENDCLASS. START-OF-SELECTION. DATA(ls_product) = lcl_product_reader=>read_product( p_matnr ). IF ls_product-matnr IS INITIAL. MESSAGE 'Product not found' TYPE 'S' DISPLAY LIKE 'E'. RETURN. ENDIF. WRITE / ls_product-matnr. WRITE / ls_product-mtart. WRITE / ls_product-meins.5. 本篇常见错排查
DLL load failed:导入 PyRFC 时报这个错,多半不是 SAP 用户密码问题,而是本机原生库加载路径或位数不匹配。SAP NW RFC SDK 是原生库,Python 解释器、PyRFC Wheel、SDK 位数必须一致。Windows 上最省心的组合是 x64 Python、x64 SAP NW RFC SDK、匹配版本的 PyRFC。Python 3.8 之后 Windows DLL 搜索机制有变化,PATH 不再按旧方式搜索 DLL,必要时显式设置 SDK 的 lib 目录。
route permission denied:SAProuter 根据路由权限表判断连接能否建立,权限不满足时返回这个错误。这时不要反复改 HTTP proxy 配置,要回到 saprouttab 检查允许的端口和路由串。/H/router.example.com/S/3299/H/appserver.example.local/S/3300这种字符串不是 HTTP Proxy 地址,塞进HTTPS_PROXY或 Node.js 的 proxy 配置只会得到各种连接失败。
HTTP HEAD 探测失败:部分 ADT 客户端会发 HEAD 请求做探测,但SADT_REST_RFC_ENDPOINT对 HEAD 支持不理想。桥接层把 HEAD 转成 GET 发给 FM,再在本地 HTTP 层丢弃 Body,客户端仍然收到符合预期的 HEAD 响应。
X-CSRF-Token 缺失:普通 HTTP 场景里 ADT 客户端写操作前会先取 CSRF Token。RFC 场景不依赖同样的 HTTP Session 机制,SADT_REST_RFC_ENDPOINT已经在 RFC 登录上下文里完成身份认证。若客户端坚持等待 Token,桥接层可以在 fetch 请求时合成一个占位 Token,让 vsp 这类 HTTP-only 客户端不会因为缺 Token 拒绝写操作。
账号锁定:每次 ADT 调用背后可能伴随 RFC 登录,密码错误还不断重试很容易触发 SAP 用户锁定。认证错误立即停下,修正.env或 MCP 配置后再试,不做自动重试循环。
桥接端口暴露:桥只监听127.0.0.1,不要暴露到局域网。.env和日志里可能含有 URI、Header 等敏感信息,本地保管,不要提交到仓库。
6. 把模型通道和 SAP 通道分开治理
这套方案里有两个独立的通道需要分别治理。模型侧走 TaoToken 统一 Key,排障和接入参考 API Keys 与接入文档:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 和 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。验证模型是否正常,用模型对话页面:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。长期编码和 Agent 场景,看 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。
SAP 侧走 RFC 用户和 ADT 授权,桥接层不绕过任何 SAP 授权,S_RFC、ADT Resource Authorization、包权限、对象锁、传输请求控制仍然生效。生产系统建议只读,写操作做成显式开关,可写包范围限制在$TMP、Z*或约定命名空间内。多人共享同一个桥会导致 SAP 侧审计混淆,企业级方案应该考虑 Destination Service、Cloud Connector、Principal Propagation 这类身份传递机制,让每个实际用户的身份可追溯。
最后留一个实际经验:桥接程序复用一个串行化 RFC 连接,简单且对锁友好,但不适合大量并发客户端同时轰击一个桥。一个桥对应一个 SAP 用户、一个 Client、一个端口,这个约束在配置阶段就定好,后面排障会省很多事。