@mcp-abap-adt/adt-strategies

中文文档
0.18.0发布于 2026/11/02LGPL-3.0-onlyTypeScriptCommonJS

Default result and failure strategies for @mcp-abap-adt/adt-clients, derived from recorded ADT answers

原始使用文档
文档目录7 节
  1. Why this is a separate package
  2. Why it cannot be one rule
  3. What is in it
  4. Why a strategy says what it says
  5. The whole surface
  6. How the defaults are justified
  7. Licence

@mcp-abap-adt/adt-strategies

Since adt-clients 36.0.0 every call below to one of the 32 members that take options.headers (create, update, updateMetadata, delete, checkDeletion, check, activate and the others listed in A request's own headers) passes the request's own Accept / Content-Type pair. In these samples headers stands for that pair, an IAdtHeaders ({ accept, contentType }); the member sends exactly it.

The readings and verdicts for @mcp-abap-adt/adt-clients: what an ADT answer becomes, and whether it is a failure — derived from recorded ADT answers.

Why this is a separate package

adt-clients interprets nothing. Every member makes one ADT request and answers the document SAP sent, unless you built the implementation with a reading; it judges nothing, unless you pass analyse with the call. That is deliberate: which part of a document you want, and whether an answer is a failure, depend on which object types you touch and what you were doing, and a library that decided for you would be wrong for somebody.

Which leaves a real gap for anyone who has no opinion yet. This package fills it — with an opinion, stated as one, and built from evidence rather than taste. Since adt-clients 23.0.0 it holds every reading and verdict that library used to apply on its own: the transport tree, the search hits, the version feed, the unit-test run id, the abapGit repositories, the ATC and profiler shapes, the package-deletion and publication verdicts. MIGRATION-23.md maps each old behaviour to the strategy that replaces it.

Two axes, two moments. A result strategy is given once, when you build an implementation — one slot per member, in the result set adt-clients exports for it (transportDocuments, utilDocuments, …). An error strategy is given with every call, as options.analyse:

import { transportDocuments } from '@mcp-abap-adt/adt-clients';
import {
  analyseDeletion,
  transportSearchConfigurations,
  transportTree,
} from '@mcp-abap-adt/adt-strategies';

const requests = client.getRequest({
  ...transportDocuments,
  list: transportTree,
  searchConfigurations: transportSearchConfigurations,
});

await client.getPackage().delete({ packageName: 'ZPKG' }, { headers, analyse: analyseDeletion });
import { analyseActivation, asItCame } from '@mcp-abap-adt/adt-strategies';

const answer = await client
  .getClass()
  .activate({ className: 'ZCL_X' }, { headers, analyse: analyseActivation });

if (!answer.ok) {
  for (const message of answer.getError().messages) {
    console.error(`[${message.type}] ${message.text}`);
  }
}

Why it cannot be one rule

ADT does not have a single way of saying no. From the recordings in corpus/adt/ (answers/ and debugger/) in adt-clients' directory of this repository:

the "no"arrives asshape
activation did not happen200<chkl:messages> with <msg type="E">
the object was never checked200chkrun:status="notProcessed"
the check found an error200<chkrun:checkMessage type="E">
the deletion was declined200del:isDeleted="false"
the object may not be deleted200del:isDeletable="false"
the name is taken (DDL, function group)200<SEVERITY>ERROR</SEVERITY>
the name is taken (class, domain, table)400<exc:exception>
locked, absent, unlocked write403 / 404 / 423<exc:exception>

Read the last three rows together. One question, asked of one endpoint family, answered two ways depending on the object type. A rule keyed on the status misses the first of them silently and reports a taken name as free.

What is in it

The error axis. One strategy per document form — analyseActivation, analyseCheck, analyseDeletion, analyseValidation, analyseUnitTest, analyseException, analysePublication, analyseCdsTestDoubles — analyseAny, which dispatches on the root element when the form is not known in advance, and five built for one question: analyseMessageClassMessage(msgno), analyseObjectProperties, analyseUnitTestStart, analysePublicationLock, analyseDebuggeeEnd, analyseDebuggerFreed and analyseUnsupportedStatus(statuses, what).

Each answers an IAdtMessageFailure: the contract's IAdtError plus every message in the document, normalised. SAP spells severity three ways and carries a T100 key in exactly one of the forms; the reading flattens that so a caller matching on type === 'E' does not have to know which carrier they got.

Why a strategy says what it says

Several of these exist because SAP answers something other than what it means — a 403 on a service binding's LOCK that is to be ignored (analysePublicationLock), a refusal inside a 200 (analysePublication, analyseActivation, analyseDeletion). Why analysePublicationLock exists is in ERRATA.md — shipped in this package, and kept in adt-clients at docs/usage/ERRATA.md.

The whole surface

exportwhat it is
analyseActivation<chkl:messages> — a <msg type="E"> is the verdict, activationExecuted="false" is not
analyseCheck<chkrun:checkRunReports> — the status first, then the messages
analyseDeletiondel:isDeleted and del:isDeletable on a 200, and every del:message, each with its severity and the T100 key read from its long-text link
analyseValidationname admissibility: two families answer with a body, three with the status
analyseUnitTestaunit:runResult — alerts on the method that failed
analyseException<exc:exception>, wherever it arrives
analysePublicationa service binding's publication job: <SEVERITY> inside a 200
analysePublicationLocka service binding's LOCK before a publish or unpublish: a 403 (an editor holds the binding) is no failure, the lock comes back without a handle; every other refusal as analyseException. A caller who wants the 403 to stop them passes analyseException instead
analyseDebuggeeEndan ABAP debugger step or termination: 500 AdiFailed subtype debuggeeEnded or terminateDebuggee is the debuggee ending as asked, no failure — the result is the document naming the subtype; every other refusal as analyseException
analyseDebuggerFreedterminateDebugger: a 500 served as text/plain or with an empty body, with no ADT exception document, is the debugger freeing its own session, no failure; everything else (a 500 with an exception document or served as anything else, other statuses, a connection failure, a body that is not a string) as analyseException
analyseCdsTestDoublesthe CDS test-doubles check
analyseMessageClassMessage(msgno)the class answering is not the message existing: a class document without the message, and an answer that is no message class document at all (empty, not XML, another document), are refused ADT_OBJECT_NOT_FOUND (since 0.14.0 the second too)
analyseObjectPropertiesa 200 from getUtils().getObjectProperties is not the object being described: an answer that is no opr:objectProperties document holding opr:object (empty, not XML, another document such as a sign-in page) is refused ADT_OBJECT_NOT_FOUND (since 0.15.0)
analyseUnitTestStarta started run whose answer names no run id
analyseUnsupportedStatus(statuses, what)renames a 404/405/501… as UNSUPPORTED_OPERATION — a system without the resource, told apart from a refusal
analyseAnydispatches on the root element when the form is not known in advance
readActivationRefusal, readCheckRunRefusal, readDeletionRefusal, readValidationRefusal, readUnitTestRefusal, readExceptionRefusal, readPublicationRefusal, readCdsTestDoublesRefusal, readMessageClassMessageAbsence, readObjectPropertiesAbsence, readAdtRefusalthe readings underneath — pure functions over a document, answering AdtRefusal or null
readExceptionSubTypethe subtype an exc:exception names (com.sap.adt.communicationFramework.subType), or undefined
isIndeterminateWalkAnswersays an empty node structure cannot be read: an empty package and one that does not exist answer identically
AdtMessage, AdtRefusal, IAdtMessageFailurethe shapes those produce

The result axis — each a IResultStrategy for one slot of an adt-clients result set:

exportslot it fillsanswers
asItCame, rawOfanythe answer unchanged; answer.data as text
transportTree, transportCreated, transportSearchConfigurations, transportObjectEntriesgetRequest: list; created/createdTask; searchConfigurations; objectsITransportTree, ICreatedTransport, ITransportSearchConfiguration[], ITransportObjectEntry[]
objectVersionsany versionable type: versionsIObjectVersion[]
unitTestRunIdAdtExecutor.getClassTestRunner: runthe run id, from the header ADT puts it in
featureToggleRuntimeState, featureToggleCheckStategetFeatureToggle: runtimeState, checkStatethe two states
utilSearchHits, utilNamedItems, utilNodeContents, utilInactiveObjects, utilActivationRunId, utilWhereUsedReferencesgetUtils: search, types, node, inactive, activation, whereUsedthe parsed shapes; readSearchHits, readNamedItems, readNodeStructure, extractRunId are the pure functions underneath
abapGitRepos, abapGitErrorLog, abapGitExternalRepoAdtAbapGitClient: repos, errorLog, externalRepoIAbapGitRepo[] (with repositoryId, pullLink, logLink), log entries, branches
atcSystemCheckVariant, atcWorklistId, atcStartedRun, atcWaitingRun, atcRunStatusgetAtc: checkVariant, worklist, startedRun, waitingRun, runStatusthe variant, the worklist id, the run
profilerTraceEntries, profilerHitList, profilerStatements, profilerDbAccesses, compareRecordedAtgetProfiler: list, hitlist, statements, dbAccessestrace entries and the three views; an ordering by recorded time
traceSchedulingTypes, traceSchedulingRequests, traceSchedulingProfilerIdexecutors: types, requests, scheduledthe catalogue, the requests, the profiler id
feedDescriptors, feedVariants, feedEntries, feedSystemMessages, feedGatewayErrors, feedGatewayErrorDetailgetFeedsthe Atom feeds, read

A code read from an answer is an IAdtAnsweredObject (since 0.16.0). The readings that take a type code from an answer — readSearchHits / utilSearchHits, readNodeStructure / utilNodeContents, utilInactiveObjects, utilWhereUsedReferences and the trace readings' program references — look it up once against the ADT type tables of @mcp-abap-adt/interfaces-adt (every variant: an answer does not say which release sent it) and answer { type: AdtTypeCode } for a code a table holds, { unknownType: string } — the text exactly as ADT sent it — for any other. answeredObject(text) is that lookup, for a consumer reading a code itself:

import { answeredObject } from '@mcp-abap-adt/adt-strategies';

const object = answeredObject('CLAS/OC');
if (object.type !== undefined) {
  // a code of the tables: act on it
} else {
  console.warn(`not a code the library serves: ${object.unknownType}`);
}

Use a named strategy where the form is known. analyseAny answers null for a document it does not recognise, and null here means "not a failure" — so a form nobody wrote a reading for passes as a success.

A reading answers null for two different things: a document that is not a refusal, and a document of a form it was not written for. A caller who needs to tell those apart calls the specific reading rather than the dispatcher.

None of the result readings is a default. adt-clients answers the document; shaping it is the consumer's decision — which fields, to what end — and these are offered by name for the consumer who wants the shape the library used to impose. Most readings answer an empty shape for a document they cannot read — judging the document is analyse's job, not the reading's. Two throw when the document has no tm:root: transportTree and transportCreated, because an empty tree or an empty request would report something that does not exist. adt-clients lets a reading's exception surface as itself.

How the defaults are justified

Every strategy is tested against the recorded answer it was derived from and against the recorded success it must be told apart from. That pairing is the point: on most of these endpoints the refusal and the success share a status, so a reading that fires on both would look correct and recognise nothing.

No SAP system. The corpus is the fixture.

One row of the table was wrong when it was first written — a table was listed under validation, and the corpus rejected it on the first run: a table answers 400 with an exception document, not <SEVERITY>ERROR</SEVERITY>. That is the argument for this package in one line.

Licence

LGPL-3.0-only.

版本与发布记录

@mcp-abap-adt/adt-strategies 共发布 21 个版本,首次发布于 2026/09/12;过去 12 个月发布了 21 个版本。

版本发布日期距今
0.18.0latest2026/11/021 天前
0.17.02026/10/313 天前
0.16.02026/10/304 天前
0.15.02026/10/2112 天前
0.14.02026/10/2112 天前
0.13.02026/10/1816 天前

关于 @mcp-abap-adt/adt-strategies 的常见问题

@mcp-abap-adt/adt-strategies 是什么?

@mcp-abap-adt/ ADT -clients的默认结果和失败策略,源自记录的ADT答案

如何安装 @mcp-abap-adt/adt-strategies?

在项目目录运行 npm install @mcp-abap-adt/adt-strategies,或使用 pnpm add / yarn add / bun add。

@mcp-abap-adt/adt-strategies 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@mcp-abap-adt/adt-strategies 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@mcp-abap-adt/adt-strategies 可以商用吗?

@mcp-abap-adt/adt-strategies 使用 LGPL-3.0-only 许可。弱 Copyleft:作为库被调用时通常不要求开源你自己的代码,但修改库本身需以 LGPL 开源;前端打包合并后的适用边界存在不同理解。以上为概括,不构成法律意见,请以许可原文为准。

@mcp-abap-adt/adt-strategies 还在维护吗?

最新版本 0.18.0 发布于 2026/11/02(1 天前);至今共发布 21 个版本,其中过去 12 个月发布了 21 个;首次发布于 2026/09/12。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。