ABAP ADT MCP

Desarrollo ABAP desde Claude y otros hosts MCP: búsqueda de objetos, lectura y escritura de código fuente, transportes, activación, ABAP Unit, ATC, dumps cortos, depurador y consultas SQL a través de ADT. 173 herramientas, multi-sistema (S/4HANA Cloud y on-premise), con salvaguardas del lado del servidor (solo lectura, paquetes permitidos, tablas denegadas) y registro de auditoría.

Documentación

abap-adt-mcp

Permite que Claude lea, escriba, pruebe y verifique código ABAP en tus sistemas SAP.

npm version CI License: MIT Node.js MCP Registry

Inglés · Portugués (Brasil) · Alemán

abap-adt-mcp es un servidor de Model Context Protocol. Ejecútalo junto a Claude Desktop, Claude Code o cualquier otro host MCP, apúntalo a uno o más sistemas SAP, y el modelo obtiene los mismos endpoints REST de ADT que usa Eclipse: buscar objetos, leer y editar código fuente, crear transportes, activar, ejecutar ABAP Unit y ATC, leer dumps cortos, consultar tablas. Un solo servidor expone 173 herramientas sobre tantos sistemas SAP como configures, tanto S/4HANA Cloud como on-prem.

Úsalo con criterio y prefiere sistemas de desarrollo. Un destino sin bloque policy es totalmente escribible dentro de tus autorizaciones SAP. Las barreras de protección por destino (solo lectura, paquetes permitidos, tablas denegadas) las aplica el propio servidor, sea cual sea la aprobación del host, de modo que un prompt descuidado no puede llegar al sistema equivocado.

Tabla de contenidos

Novedades en 2.0.0

Publicado el 2026-09-08. La lista completa está en CHANGELOG.md; lo que importa al actualizar:

  • Se requiere Node.js 22.12 o más reciente (cambio importante). Node 18 y 20 han llegado al final de su vida útil y no reciben parches de seguridad; un servidor que guarda credenciales SAP no debería ejecutarse en ellos. Con un Node más antiguo, npm imprime EBADENGINE y el servidor no está probado; instala el LTS actual y reinicia el host. La imagen de contenedor ya estaba en node:22-alpine.
  • tls.servername en un destino. Para un sistema al que se llega por dirección IP o nombre de host corto cuyo certificado lleva el nombre totalmente cualificado: el nombre se verifica y se envía como SNI, la verificación permanece activada y insecureTls ya no es la única vía para ese paisaje. listSystems muestra servername NAME.
  • Los errores de certificado enseñan la solución. Un handshake fallido llega al modelo como kind: "tlsCertificate" con una pista que nombra el destino: emisor desconocido da la línea openssl s_client para ese host y apunta a tls.ca; una discrepancia de nombre cita los nombres que Node informó y apunta a tls.servername; un certificado caducado dice que solo la renovación lo soluciona. insecureTls se menciona al final.
  • insecureTls permanece, por destino, desactivado por defecto, anunciado al inicio; SECURITY.md documenta por qué.
  • Cadena de suministro. puppeteer-core 25 elimina la última alerta abierta de Dependabot del árbol de dependencias (npm audit informa cero vulnerabilidades); Dependabot ahora espera un período de enfriamiento antes de proponer actualizaciones y agrupa las actualizaciones de seguridad en una sola solicitud de extracción; dotenv se carga silenciosamente para que stdout siga siendo un canal JSON-RPC limpio.

Actualizar desde 1.x no requiere cambios de configuración: systems.json, las políticas, los nombres de herramientas y las variables de entorno no cambian.

Configuración

Tres cosas antes de empezar:

  • Node.js 22.12 o más reciente (22 o 24 LTS; 2.0.0 eliminó Node 18 y 20). Descarga el instalador LTS desde nodejs.org; incluye npm y npx, que es todo lo que necesita el host. No se requiere terminal para comprobarlo: si Node falta, el registro del host dice spawn npx ENOENT cuando intenta iniciar el servidor (ver paso 2).
  • Acceso al sistema SAP. En S/4HANA Cloud (edición pública) no hay nada que configurar en el lado SAP para usuarios con nombre: tu usuario necesita el rol de negocio que permite Eclipse ADT en el tenant (SAP_BR_DEVELOPER en la entrega estándar); si Eclipse ADT funciona para ti, este servidor también funciona. On-prem, el servicio /sap/bc/adt debe estar activo en la transacción SICF (una tarea de Basis) y tu usuario necesita las autorizaciones habituales de desarrollo ADT. Solo los clientes oauth desatendidos necesitan un Acuerdo de Comunicación; ver Autenticación.
  • Un navegador Chromium (Chrome, Edge o Brave) en la máquina cuando uses SSO de navegador.

1. Describe tus sistemas SAP

Crea una carpeta .abap-adt-mcp en tu directorio personal y un archivo systems.json dentro, una entrada por sistema (un "destino"). Sin terminal: en macOS abre Finder, pulsa Shift-Cmd-G, introduce ~, crea la carpeta (Finder te pide confirmar un nombre que empieza por punto; Shift-Cmd-. muestra las carpetas ocultas), luego guarda el archivo allí desde cualquier editor de texto. En Windows la carpeta es C:\Users\<you>\.abap-adt-mcp, creada en el Explorador de archivos como cualquier otra. Un tenant de S/4HANA Cloud con SSO de navegador necesita exactamente esto:

{
  "DEV": {
    "url": "https://myXXXXXX.s4hana.cloud.sap",
    "client": "080",
    "authType": "sso",
    "default": true
  }
}

url es obligatorio; client es el cliente en el que aterriza tu sesión SSO (en los tenants probados, el sistema de desarrollo iniciaba sesión en 080 y los sistemas de customizing y prueba en 100; la entrada About en el menú de usuario del launchpad lo muestra); authType por defecto es sso y "default": true te permite omitir el nombre del destino en cada llamada. La clave (DEV) es tu elección y es el nombre que usarás en los chats. Varios sistemas, con barreras de protección, se ven así (o copia systems.example.json):

{
  "DEV": {
    "url": "https://myXXXXXX.s4hana.cloud.sap",
    "client": "080",
    "authType": "sso",
    "default": true,
    "policy": { "allowedPackages": ["Z*"] }
  },
  "PRD": {
    "url": "https://myYYYYYY.s4hana.cloud.sap",
    "client": "100",
    "authType": "sso",
    "policy": { "readOnly": true, "deniedTables": ["PA*", "HR*", "USR02"], "allowFreeSql": false }
  },
  "ONPREM": {
    "url": "https://sap.example.com:44300",
    "client": "100",
    "authType": "basic",
    "user": "DEVELOPER",
    "password": "${env:ONPREM_PASSWORD}",
    "policy": { "allowedPackages": ["Z*", "$*"] },
    "tls": { "ca": "/etc/ssl/corp-ca.pem" }
  }
}

El patrón para cualquier sistema productivo o de prueba es la entrada PRD: añade "policy": { "readOnly": true } y el servidor rechaza toda escritura allí, sea cual sea la petición al modelo. sso abre un navegador real una vez para usuarios con nombre de S/4HANA Cloud; basic es para usuarios on-prem y Communication Users; oauth es para clientes desatendidos. ${env:VAR} extrae un secreto del entorno para que nunca quede en el archivo, policy lo aplica el servidor, y tls.ca añade una CA corporativa con la verificación activada (tls.servername nombra el certificado cuando se llega al sistema por dirección IP). $* (paquetes locales) solo aparece en la entrada on-prem porque el tenant probado de Public Cloud rechaza $TMP.

Si tienes terminal, restringe el archivo a tu usuario:

chmod 600 ~/.abap-adt-mcp/systems.json

Puedes omitir este paso cuando el archivo no contenga contraseñas en línea (un archivo solo SSO, o secretos referenciados como ${env:VAR}): el servidor entonces solo imprime una advertencia si el archivo es legible por otros. Solo se niega a iniciar cuando un archivo legible por otros contiene contraseñas en línea, secretos de cliente o contraseñas de git. Windows no tiene modos de archivo; allí se omite la comprobación.

2. Registra el servidor en tu host

El paquete está en npm como abap-adt-mcp (publicado mediante trusted publishing con procedencia), así que npx es todo lo que necesitas.

Claude Code, una línea:

claude mcp add abap-adt-mcp -e SAP_SYSTEMS_FILE=$HOME/.abap-adt-mcp/systems.json -- npx -y abap-adt-mcp

Claude Desktop (Settings > Developer > Edit Config, luego cierra y reabre la aplicación). Reemplaza me con tu propio nombre de usuario; en Windows escribe la ruta como C:/Users/<you>/.abap-adt-mcp/systems.json:

{
  "mcpServers": {
    "abap-adt-mcp": {
      "command": "npx",
      "args": ["-y", "abap-adt-mcp"],
      "env": { "SAP_SYSTEMS_FILE": "/Users/me/.abap-adt-mcp/systems.json", "MCP_TOOLSETS": "focused" }
    }
  }
}

MCP_TOOLSETS=focused publica las 114 herramientas de desarrollo en lugar de las 173, lo que evita que los esquemas de herramientas consuman la ventana de contexto del chat; elimínalo cuando necesites los conjuntos de herramientas de depurador, trazas, abapGit, RAP o refactorización. El mismo JSON funciona en Cursor, Cline y otros hosts que leen un mapa mcpServers; VS Code llama al mapa servers en su lugar, así que renombra la clave de nivel superior allí (docs/HOSTS.md tiene la forma por host). La clave abap-adt-mcp es el nombre que el host muestra para el servidor y el prefijo de cada herramienta (mcp__abap-adt-mcp__searchObject en Claude Code); los skills ABAP públicos escritos para este servidor buscan ese nombre, así que una clave distinta solo impide que esos skills reconozcan el servidor; nada más se rompe.

Tras el reinicio, Claude Desktop lista abap-adt-mcp con un estado en Settings > Developer, y el menú de herramientas bajo el campo de chat (el icono de controles deslizantes) muestra el servidor con sus herramientas. Si no aparece nada, lee el registro del host: al momento de escribir esto, Claude Desktop escribe mcp.log y mcp-server-abap-adt-mcp.log en ~/Library/Logs/Claude en macOS y %APPDATA%\Claude\logs en Windows, y Claude Code muestra el estado con /mcp. Todo lo que imprime el servidor (advertencias de inicio, la advertencia del archivo de auditoría, mensajes MCP_PROFILE_GATE=warn) va a stderr y termina en ese registro. Tanto Claude Desktop como Claude Code preguntan antes de ejecutar una herramienta que no hayas aprobado permanentemente; ese diálogo es comportamiento del host e independiente de la anotación destructiveHint, así que trátalo como una cortesía y el bloque policy como la garantía.

3. Saluda

Abre un chat nuevo y escribe (reemplaza DEV con la clave que elegiste en systems.json):

Lista mis sistemas SAP, inicia sesión en DEV y muéstrame el código fuente de la clase CL_ABAP_CHAR_UTILITIES.

El modelo llama a listSystems, login (aparece una ventana de navegador para destinos SSO; marca "mantener la sesión iniciada" y los inicios de sesión posteriores serán silenciosos), searchObject y getObjectSource. Cuando vuelve el código fuente, has terminado. login es opcional en todos los modos: el dispatcher realiza el inicio de sesión del navegador antes de la primera llamada en un destino SSO, y los destinos basic y oauth se autentican en su primera solicitud. Llámalo explícitamente solo para forzar un inicio de sesión nuevo o para probar las credenciales antes que nada. Pedir healthcheck devuelve la versión del servidor, los nombres de los destinos, el destino predeterminado, los conjuntos de herramientas activos y el número de herramientas; systemProfile indica si un destino es S/4HANA Cloud u on-prem y qué conjuntos de herramientas no puede servir.

Qué pedirle al modelo

El servidor es una caja de herramientas de la que el modelo elige: pide en lenguaje natural y él selecciona la secuencia. Cosas que funcionan bien desde la primera sesión:

PreguntaHerramientas a las que recurre el modelo
"Explica qué hace el método GET_DATA de ZCL_ORDER_SERVICE."searchObject, getMethodSource
"¿Dónde se sigue usando la tabla ZTABLE y en qué programas?"whereUsed, sourceTextSearch, grepPackage
"Muéstrame los campos y asociaciones de la vista CDS ZI_PRODUCT."cdsViewInfo, objectStructureElements
"Añade una comprobación de nulos al inicio de GET_DATA, actívala y ejecuta las pruebas unitarias."resolveTransport, syntaxCheckCode, editObjectSource (con activate=true), unitTestRun, objectDiff
"Crea la clase ZCL_HELLO en el paquete ZDEMO que imprima Hello World, con una prueba unitaria."validateNewObject, resolveTransport, createObject, setObjectSource, createTestInclude, unitTestRun
"Ejecuta ATC en el paquete ZFIN y aplica todas las correcciones rápidas que sean seguras."createAtcRun, atcWorklists, atcQuickfixProposals, atcApplyQuickfix, atcSummary
"¿Qué cambió en el transporte DEVK900123? Revísalo y dime si es seguro liberarlo."transportDetails, transportUnifiedDiff
"¿Por qué ocurrió el último volcado corto del usuario DEVELOPER? Propón una corrección."dumps, dumpDetails, getObjectSource
"¿Está ZCL_ORDER_SERVICE listo para ABAP Cloud? ¿Qué objetos SAP lo bloquean?"apiReleaseState, createAtcRun
"Selecciona las diez filas más recientes de ZTABLE donde STATUS = 'X'."runQuery (o tableContents cuando la vista previa de datos rechace una tabla)
"Prueba este fragmento y muéstrame la salida."runSnippet
"¿Qué conjuntos de herramientas soporta DEV? ¿Está disponible el depurador allí?"systemProfile

En un sistema on-prem normal, el ejemplo de creación también funciona con $TMP y sin transporte; el tenant de S/4HANA Cloud probado rechazó $TMP, así que allí debes nombrar un paquete de cliente y su transporte (ver S/4HANA Cloud versus on-prem).

Hábitos que el servidor incorpora, para que no tengas que especificarlos: las herramientas de escritura se bloquean y desbloquean por sí mismas; activate=true activa en la misma llamada; cada error es JSON con kind, hint y nextTools, de modo que el modelo se recupera en lugar de reintentar a ciegas; las sesiones caducadas se reautentican y la llamada se reintenta una vez; los resultados grandes se paginan dentro de un presupuesto de 40.000 caracteres (MCP_MAX_RESPONSE_CHARS) e informan de hasMore; las llamadas largas envían notificaciones de progreso MCP a los hosts que pasan un progressToken (además de un latido cada 10 segundos). Los flujos canónicos de creación y edición viajan en el campo instructions de MCP, y cada herramienta lleva anotaciones readOnlyHint/destructiveHint para que los hosts que controlan la aprobación por anotación solo pregunten en escrituras.

Flujos de trabajo en detalle

Las secuencias completas herramienta por herramienta, las formas de los argumentos y las recetas están en docs/WORKFLOWS.md; esta sección es la versión resumida.

Toda herramienta excepto listSystems y healthcheck acepta un destination opcional; es obligatorio cuando hay varios sistemas configurados y ninguno está marcado como default (o nombrado en SAP_DEFAULT_DESTINATION).

URLs y nombres. searchObject devuelve la URL del objeto, por ejemplo /sap/bc/adt/oo/classes/zcl_example; la URL de la fuente es esa más /source/main; los includes de clase (implementaciones, clases de prueba) usan las URLs de classIncludes tal cual. Las herramientas heredadas de varias generaciones anteriores nombran esa URL de forma diferente (objSourceUrl, objectSourceUrl, objectUrl, classUrl, url, mainUrl), así que el despachador mapea los nombres al esquema de cada herramienta y elimina o añade /source/main donde sea necesario: el valor de searchObject se puede pasar a cualquiera de ellas. Las herramientas a nivel de clase (getMethodSource, setMethodSource, whereUsed, cdsViewInfo) también aceptan el nombre simple.

Buscar y leer código. searchObject encuentra objetos por nombre. Por contenido, sourceTextSearch usa el índice de texto ADT y grepPackage busca en las fuentes del paquete en el cliente con líneas de contexto (el recurso alternativo cuando un tenant no tiene índice de texto). packageTree, whereUsed, cdsViewInfo, typeHierarchy y classComponents ofrecen navegación estilo IDE. getObjectSource lee una fuente (paginada con startLine/maxLines, version=inactive para código no activado), getMethodSource un método, y exportPackageSources escribe un árbol de paquetes en disco con el diseño de abapGit para herramientas locales.

Editar de forma segura. Las escrituras bloquean, escriben y desbloquean por sí mismas y activan cuando pasas activate=true:

  1. resolveTransport(objSourceUrl) devuelve el transporte que ya registra el objeto, el más reciente modificable para su paquete, o needsTransport: false para paquetes locales; createIfMissing=true crea uno cuando no existe ninguno.
  2. syntaxCheckCode sobre la fuente prevista: opcional para un cambio de una línea, seguro barato para algo más grande.
  3. editObjectSource(objectSourceUrl, replacements=[{oldText, newText}], activate=true, transport) para cambios específicos (el servidor relee SAP primero; cada oldText debe coincidir exactamente una vez, de lo contrario la llamada falla con "0 coincidencias" o los números de línea de cada coincidencia y no se escribe nada), setMethodSource(classUrl, methodName, source, activate=true, transport) para intercambiar un bloque METHOD ... ENDMETHOD en la implementación (pasa el bloque completo o solo el cuerpo; la parte de definición permanece como está; include y className seleccionan clases locales o de prueba; un método desconocido se rechaza con la lista de métodos presentes), setObjectSource para reescrituras completas.
  4. Lee el campo activation del resultado; corrige y escribe de nuevo, o activateByName / activatePackage más tarde.
  5. unitTestRun(url), luego objectDiff(objectUrl) para mostrar qué cambió respecto a la revisión anterior.

lock/unLock solo mantienen un bloqueo entre varias escrituras; listLocks y forceUnlock se recuperan de una escritura fallida. Un bloqueo mantenido por otra sesión (una ventana de Eclipse abierta, por ejemplo) se informa como externo: dropSession y forceUnlock no pueden liberarlo, solo esa sesión o SM12 pueden.

Crear objetos y transportes. loadTypes (elige el objtype, por ejemplo CLAS/OC), validateNewObject, luego resolveTransport(objSourceUrl="/sap/bc/adt/packages/<pkg>", devClass="<pkg>") para el paquete en sí, ya que el objeto aún no tiene URL (o createTransport), luego createObject(objtype, name, parentName=<pkg>, description, parentPath="/sap/bc/adt/packages/<pkg>", responsible, transport), setObjectSource con activate=true, createTestInclude, unitTestRun. creatableTypeDetails indica qué campos requiere cada tipo; los paquetes (DEVC/K) necesitan swcomp, y los backends en la nube necesitan responsible.

Pruebas unitarias y ATC. unitTestRun después de cada cambio (paginado con startIndex/maxItems); unitTestEvaluation profundiza en los resultados. ATC: createAtcRun(mainUrl, variant) sobre un objeto, paquete o transporte (un nombre de variante como ABAP_CLOUD_DEVELOPMENT_DEFAULT se resuelve a una lista de trabajo por ti), luego atcWorklists o atcSummary (totales por prioridad, comprobación y objeto), atcQuickfixProposals y atcApplyQuickfix para correcciones deterministas, atcDocumentation para comprobaciones desconocidas; las exenciones pasan por atcExemptProposal y atcRequestExemption.

Revisar un transporte. transportDetails lista objetos, propietario, tareas y estado; transportUnifiedDiff compara cada objeto fuente registrado en el transporte contra la versión anterior a él, incluyendo includes de clase LIMU y métodos, includes REPS y módulos FUNC (los mensajes y DDIC se omiten con un motivo). La comparación es contra la fuente actual, así que en un transporte ya liberado, los cambios posteriores a los mismos objetos también aparecen. Se ejecuta en tenants de S/4HANA Cloud (la cobertura de LIMU surgió de una sesión RAP allí, ver docs/FIELD-NOTES.md). objectDiff cubre objetos con varias revisiones. userTransports, transportRelease, transportSetOwner y transportAddUser completan el panorama.

Datos. runQuery(sqlQuery) ejecuta un SELECT SQL ABAP a través de la vista previa de datos ADT sobre tablas y vistas CDS (por nombre de entidad, incluidas las vistas de API liberadas), por ejemplo SELECT carrid, connid, fldate FROM sflight WHERE carrid = 'LH' ORDER BY fldate DESCENDING. rowNumber limita cuántas filas devuelve SAP (por defecto 100) y startRow/maxRows paginan el resultado. Las sentencias se ajustan al límite de 255 caracteres por línea de la vista previa antes de enviarse (un literal único más largo que eso aún falla). Las tablas cuyo dataMaintenance DDIC está restringido son rechazadas por la vista previa: tableContents(ddicEntityName) las lee (S_TABU_DIS/S_TABU_NAM siguen aplicándose). Las claves vuelven en formato interno, así que getDataElementProperties y getDomainProperties te informan sobre ceros iniciales y conversiones de salida.

Volcados y depurador. dumps(from, to, user, contains) devuelve resúmenes compactos (error de runtime, excepción, programa, punto de terminación con URL de fuente y línea, parte superior de la pila) y dumpDetails(dumpId) el análisis completo; getObjectSource alrededor de terminatedAt.line y whereUsed encuentra la causa. Los conjuntos de herramientas debugger y traces existen solo donde el backend los expone (systemProfile lo indica) y solo cuando el conjunto está publicado (focused deja ambos fuera). Sin depurador, los caminos son: un volcado (dumps), reproducir el error con runSnippet o runClass en un sistema de desarrollo y leer la salida, y traces donde el backend los sirve. Cuando el depurador está disponible, debuggerListen necesita debuggingMode, terminalId, ideId y user, como en Eclipse.

Preparación para ABAP Cloud. apiReleaseState acepta una de cuatro entradas: names (separada por comas, opcionalmente tipada como TABL:MARA), objectUrl, source (texto ABAP pegado) o sourceUrl (una URL .../source/main que el servidor lee y escanea). Comprueba los objetos SAP contra el repositorio oficial de cloudificación de SAP (liberado, deprecado con sucesores, classicAPI, noAPI; ediciones cloud, btp, pce2023, pce2022) más la respuesta /sap/bc/adt/apireleases del backend, de modo que el modelo nunca recuerde estados de liberación de memoria.

Ejecutar código. runSnippet(code, packageName) envuelve ABAP desechable en una clase IF_OO_ADT_CLASSRUN temporal, la crea, activa y ejecuta, devuelve la salida de consola y elimina la clase de nuevo, también si la activación o la ejecución fallan (una eliminación fallida se informa como cleanupError; keep=true la conserva). En on-prem, packageName por defecto es $TMP; en S/4HANA Cloud pasa un paquete de cliente, su transport y responsible, y la creación y eliminación se registran en ese transporte. runClass ejecuta una clase existente. Ambos necesitan S_DEVELOP, así que solo en sistemas de desarrollo.

abapGit, generador RAP, refactorización, servicios. abapGit: gitRepos, gitCreateRepo, gitPullRepo, stageRepo, pushRepo, checkRepo, switchRepoBranch, con gitUser/gitPassword por destino que mantienen las credenciales remotas fuera de la conversación. Generador RAP: rapGenIsAvailable, rapGenGetContent, rapGenValidateContent, rapGenPreview, rapGenGenerate (transporte requerido), luego activateObjects sobre los objetos generados y rapGenPublishService. Refactorización: renameEvaluate, renamePreview, renameExecute; el mismo trío para extractMethod*; changePackagePreview y changePackageExecute. Servicios de negocio: fetchServiceDetails(name), bindingDetails, publishServiceBinding, unPublishServiceBinding.

Prompts integrados

Seis flujos de trabajo listos viajan como prompts MCP. Cada uno nombra las herramientas exactas a llamar, en orden, y dice dónde debe detenerse y preguntar:

PromptArgumentosQué haceDónde se detiene
create-objectopcional destination, luego objectType (ID de tipo ADT como CLAS/OC, INTF/OI, PROG/P, DDLS/DF), name, package, opcional purposeValidar, crear, escribir, activar, probar unitariamente y verificar con ATC un objeto nuevo en el paquete y transporte correctos.Crea y activa; nunca elimina ni libera.
safe-editopcional destination, luego object (nombre o URL), changeLeer, modificar con reemplazos anclados a texto, activar, probar y mostrar el diff.Nunca llega a deleteObject, transportRelease o forceUnlock por sí solo; si surge un bloqueo externo o una pregunta de liberación, se detiene y pregunta.
review-transportopcional destination, luego transport (número de solicitud)Comparar cada objeto de un transporte y producir una revisión de aprobación/rechazo.Nunca llama a transportRelease.
fix-atcopcional destination, luego target (URL de objeto, nombre de paquete o transporte), opcional variantEjecutar ATC, aplicar correcciones rápidas deterministas, corregir el resto con ediciones, re-ejecutar hasta que las prioridades 1 y 2 estén limpias.Aplica correcciones rápidas y ediciones; exenciones solo con aprobación.
clean-core-checkopcional destination, luego target (nombre o URL de objeto, o nombre de paquete)Evaluar la preparación para ABAP Cloud: APIs liberadas, objetos obsoletos, sucesores, verificaciones ATC en la nube.No cambia código.
debug-dumpopcional destination, luego opcional filter (usuario, programa, excepción o ventana de tiempo)Encontrar la causa raíz de un volcado corto y proponer la corrección en la línea exacta.Propone reemplazos; no los aplica sin aprobación.

La forma de invocarlos depende del host. Claude Code expone los prompts de MCP como comandos de barra llamados /mcp__<server>__<prompt>, con los argumentos dados posicionalmente en el orden que el prompt declara (destination viene primero en cada prompt, como en la tabla):

/mcp__abap-adt-mcp__safe-edit DEV ZCL_ORDER_SERVICE "return early when the input table is empty"

Claude Desktop los ofrece desde el menú de adjuntos (más) del chat bajo el nombre del servidor al momento de escribir esto; los hosts sin soporte de prompts simplemente no los muestran, y los mismos flujos aún llegan al modelo a través del campo instructions del servidor.

Otras formas de instalar

Fijar la versión. npx -y abap-adt-mcp obtiene la versión más reciente en cada inicio. Para un despliegue controlado, fíjela (npx -y abap-adt-mcp@X.Y.Z, o la etiqueta de contenedor vX.Y.Z) y verifique la atestación de procedencia que la publicación confiable adjunta con npm audit signatures en un directorio donde el paquete esté instalado.

Plugin de Claude Code. El repositorio es su propio mercado de plugins (.claude-plugin/marketplace.json junto a plugin.json), así que dos comandos en Claude Code registran el servidor y cargan ambas habilidades, sin claude mcp add:

/plugin marketplace add williansaez/abap-adt-mcp
/plugin install abap-adt-mcp@abap-adt-mcp

El manifiesto inicia el servidor como npx -y abap-adt-mcp@<version>, fijado a la versión con la que se distribuye (la fijación se mueve con cada versión y CI lo verifica contra package.json), así que un host de plugin mantiene la versión que instaló en lugar de tomar lo que npm sirva como última versión en su próximo inicio; establece SAP_SYSTEMS_FILE=${HOME}/.abap-adt-mcp/systems.json y no MCP_TOOLSETS, por lo que publica las 173 herramientas; systems.json del paso 1 sigue siendo suyo para escribir. Las habilidades solas se instalan, al momento de escribir esto, con npx skills add williansaez/abap-adt-mcp (un instalador de terceros, no parte de este repositorio) o copiando los dos directorios bajo skills/ en ~/.claude/skills/.

Contenedor. Las imágenes se construyen desde node:22-alpine, se ejecutan como el usuario no privilegiado node (uid 1000) y se publican en GHCR en cada versión (etiquetas latest y vX.Y.Z). Monte su systems.json de solo lectura y pase los secretos referenciados a través de:

docker run -i --rm \
  -v "$PWD/systems.json:/config/systems.json:ro" \
  -e SAP_SYSTEMS_FILE=/config/systems.json \
  -e ONPREM_PASSWORD \
  ghcr.io/williansaez/abap-adt-mcp:latest

La verificación de modo de archivo también se ejecuta dentro del contenedor: un archivo montado con modo 0600 propiedad de otro uid no puede ser leído por el usuario node en absoluto (el inicio falla con is not valid JSON: EACCES, ya que la lectura y el análisis comparten una ruta de error), y un archivo legible por otros solo advierte a menos que contenga secretos en línea. O bien sea propietario del archivo con uid 1000 y mantenga 0600, o refiera cada secreto como ${env:VAR} y acepte la advertencia. Los secretos pasados con -e son visibles para docker inspect; no hay alternativa basada en archivos para MCP_HTTP_TOKEN, así que trate el entorno del contenedor como confidencial. Para Streamable HTTP dentro del contenedor, agregue -e MCP_HTTP_PORT=2236 -e MCP_HTTP_HOST=0.0.0.0 -e MCP_HTTP_TOKEN=<token> -p 127.0.0.1:2236:2236. El SSO del navegador necesita un navegador local, así que ejecute destinos SSO desde npm en la estación de trabajo; los destinos basic y oauth funcionan dentro del contenedor.

Registro MCP. Listado como io.github.williansaez/abap-adt-mcp para hosts que navegan el registro; server.json es el manifiesto del registro.

Desde el código fuente.

git clone https://github.com/williansaez/abap-adt-mcp.git
cd abap-adt-mcp
npm ci
npm run build

Luego apunte el host a node /absolute/path/abap-adt-mcp/dist/index.js. Un systems.json junto al checkout se recoge automáticamente; .env (ver .env.example) funciona para configuraciones de un solo sistema. Ambos están ignorados por git.

Autenticación

Cada destino elige su propio authType (sso a menos que SAP_AUTH_TYPE diga lo contrario). Los detalles y pasos del lado de SAP están en docs/AUTH.md.

ModoÚselo paraQué configuraConfiguración del lado de SAP
sso (predeterminado)Usuarios nombrados de S/4HANA Cloud, exactamente como Eclipse ADT (SAML2/OIDC vía IAS)Un navegador Chromium (Chrome, Edge, Brave) se abre una vez por host; las cookies de sesión se leen a través del protocolo DevTools y se mantienen en memoria, con sap-client fijado en cada solicitud. La sesión del proveedor de identidad vive en un perfil dedicado bajo ~/.abap-adt-mcp/sso/<host> (modo 0700). SAP_BROWSER_PATH anula el navegador, SAP_BROWSER_PROFILE_DIR reutiliza un perfil personalizado con claves de acceso guardadas (el perfil predeterminado del navegador se rechaza a propósito).Ninguno más allá del rol de negocio de desarrollador que su usuario ya necesita para Eclipse ADT
basicAS ABAP local, Usuarios de Comunicación de S/4HANA Clouduser y password (use ${env:VAR}). Autentica en la primera llamada, login es opcional.Un usuario con autorizaciones de ADT
oauthClientes desatendidos de S/4HANA Cloudoauth.tokenUrl, oauth.clientId, oauth.clientSecret, opcional oauth.scope (concesión de credenciales de cliente; el token se almacena en caché hasta poco antes de expirar y se invalida en un 401).Un Usuario de Comunicación, un Sistema de Comunicación con OAuth 2.0 y un Acuerdo de Comunicación para el escenario que expone ADT en su tenant (varía por tenant y no se lista aquí; el acuerdo da el endpoint de token). Las herramientas luego se ejecutan con las autorizaciones del Usuario de Comunicación.

Los usuarios de negocio nombrados en S/4HANA Cloud no pueden usar autenticación básica; inician sesión a través de sso o usted crea un Usuario de Comunicación. La sesión SSO se crea para el cliente de inicio de sesión del tenant, que puede diferir del que espera (100 en lugar de 080, por ejemplo): establezca client al que la sesión realmente usa. Un cliente incorrecto aparece como errores de autorización o no encontrado en objetos que puede abrir en Eclipse, después de un inicio de sesión que en sí tuvo éxito. El directorio de perfil SSO es un directorio de datos de usuario ordinario de Chromium: contiene las cookies y el almacenamiento local que el proveedor de identidad establece cuando marca "mantener sesión iniciada", nada que el servidor agregue, y está protegido por permisos de archivo y por lo que Chromium haga en su SO, no cifrado por el servidor; cuánto tiempo la sesión sigue válida es política del proveedor de identidad, y eliminar el directorio es la única forma de terminarla temprano (la cookie de sesión SAP cosechada nunca se escribe en disco). tls por destino agrega una CA corporativa (ca), el nombre para verificar el certificado cuando url contiene una dirección IP o nombre de host corto (servername, también enviado como SNI), o un certificado de cliente X.509 (cert + key, o pfx + passphrase), con la verificación mantenida activa; la ventana del navegador SSO gestiona su propio almacén de confianza. Opcional gitUser/gitPassword suministra credenciales de abapGit para que nunca pasen por el modelo. Las sesiones expiradas en cualquier modo se restablecen una vez y la llamada se reintenta; si eso falla, el error dice kind: "sessionExpired".

Manteniéndolo seguro

Este servidor da a un modelo de lenguaje acceso de lectura y escritura a SAP. Algunas reglas hacen que sea cómodo:

  • Las barreras de protección viven en el servidor, no en el host. El bloque policy de un destino se evalúa en el servidor antes de la llamada SAP propia de la herramienta, lo que sea que el host apruebe; allowedPackages es la única puerta que puede necesitar una búsqueda (transportInfo, en caché) para aprender el paquete de un objeto existente primero. Los rechazos vuelven como kind: "policyDenied" nombrando la puerta, y listSystems muestra cada política. Un destino sin bloque policy es completamente escribible.

    ClaveTipoEfecto
    readOnlybooleanoSolo las herramientas anotadas como solo lectura pueden ejecutarse, más login, logout, dropSession, listSystems, healthcheck, systemProfile y exportPackageSources (que escribe solo localmente). Bloqueadas como escrituras: cada escritura de fuente, lock, runSnippet, runClass, unitTestRun, createAtcRun y atcSummary. Aún permitidas: runQuery y tableContents (son lecturas; niéguelas con allowFreeSql: false o deniedTools).
    deniedToolsglobsHerramientas rechazadas por completo en este destino: un nombre, un glob (rapGen*) o toolset:<name> para cada herramienta de un conjunto, por ejemplo ["transportRelease", "toolset:git"]. Cinco herramientas de abapGit no tienen prefijo git (pushRepo, stageRepo, checkRepo, remoteRepoInfo, switchRepoBranch), así que git* solo deja abierta la ruta de push. Las herramientas permanecen listadas.
    allowFreeSqlbooleanofalse rechaza runQuery y tableContents con sqlQuery.
    deniedTablesglobsAplicado a tableContents, a cada objetivo FROM/JOIN de un runQuery, y (mejor esfuerzo, escaneando el texto ABAP) a runSnippet, setObjectSource y setMethodSource. SQL dinámico y vistas sobre la tabla no se detectan: para datos que no deben salir de SAP, confíe en las autorizaciones de visualización SAP del usuario conectado y combine allowFreeSql: false con deniedTools: ["runSnippet"] o readOnly.
    allowedPackagesglobs, lista cerradaBloquea escrituras solo; lecturas y navegación de cualquier objeto (objetos SAP incluidos) nunca se bloquean. Los argumentos de paquete se verifican directamente; las escrituras de objetos resuelven el paquete del objeto a través de transportInfo; un paquete no resoluble se rechaza. gitPullRepo, rapGenGenerate, rapGenPublishService, publishServiceBinding y unPublishServiceBinding no pueden derivar un paquete y se rechazan siempre que esta clave esté establecida.
    allowedTransportsglobsCada argumento transport/transportNumber debe coincidir; createTransport y resolveTransport(createIfMissing=true) se rechazan.

Los interruptores a nivel de servidor son MCP_READ_ONLY=1 (añade readOnly a cada destino) y MCP_DISABLED_TOOLSETS (oculta conjuntos de herramientas completos de cada destino); no hay un deniedTools, deniedTables o allowedPackages global, esos se repiten por entrada. Oculto y rechazado difieren: un conjunto de herramientas excluido por MCP_TOOLSETS/MCP_DISABLED_TOOLSETS está ausente de la lista de herramientas y una llamada por nombre (desde un prompt, un host que almacenó en caché una lista anterior o una skill) se rechaza con el nombre del conjunto de herramientas; deniedTools mantiene la herramienta listada y la rechaza en ese destino; las herramientas que un destino no puede servir (detectadas por systemProfile) permanecen listadas y se rechazan antes de llamar a SAP (MCP_PROFILE_GATE=enforce|warn|off).

  • Los secretos se mantienen fuera de archivos y chats. ${env:VAR} funciona en cada cadena de systems.json (password, oauth.clientSecret, gitPassword, tls.passphrase, incluso url); una variable faltante falla al inicio por nombre, nunca por valor. Mantén systems.json en modo 0600: un archivo legible por grupo o por todos se advierte y se rechaza cuando contiene un password, oauth.clientSecret o gitPassword en línea. Prefiere SAP_SYSTEMS_FILE sobre SAP_SYSTEMS en línea en las configuraciones del host. MCP_HTTP_TOKEN es una variable de entorno, no una entrada de archivo; el lado del cliente del transporte HTTP tiene que llevar el token en su configuración de host, así que mantén ese archivo en 0600 también. listSystems y healthcheck no reportan credenciales, los mensajes de error pasan por un paso de redacción que enmascara tokens de portador, cookies, contraseñas y URLs de user:password@host, y exportPackageSources solo puede escribir dentro de MCP_EXPORT_ROOT (por defecto ~/.abap-adt-mcp/exports, verificado contra enlaces simbólicos). reentranceTicket permanece deshabilitado a menos que SAP_ALLOW_REENTRANCE_TICKET=1, porque devuelve una credencial de inicio de sesión en vivo a la conversación.
  • TLS permanece activado y no se puede desactivar para todo a la vez. NODE_TLS_REJECT_UNAUTHORIZED=0 se elimina del entorno antes de la primera conexión, y el servidor lo dice al inicio: el problema de un destino nunca silencia la verificación para los demás, para la solicitud de token OAuth o para la descarga de cloudification. Para un certificado corporativo o autofirmado usa tls.ca en ese destino, para un certificado emitido a un nombre distinto del que está en url usa tls.servername (la verificación permanece activada en ambos casos, sin advertencia), o como último recurso insecureTls: true solo en ese destino (anunciado al inicio, mostrado por listSystems). Un handshake fallido regresa como kind: "tlsCertificate" con la solución para ese destino explicada.
  • El contenido de SAP es entrada no confiable. Comentarios, filas de tablas y feeds pueden llevar texto que intenta influir en el modelo. Usa un host que pregunte antes de las llamadas a herramientas y revisa las destructivas (deleteObject, transportRelease, transportDelete, setObjectSource, editObjectSource, setMethodSource, pushRepo, forceUnlock) antes de aprobarlas.
  • Privilegio mínimo, y qué sigue leyendo "solo lectura". Conecta con usuarios que tengan solo las autorizaciones que la tarea necesita. runQuery y tableContents leen datos comerciales reales, así que configura solo destinos donde eso sea aceptable, y exportPackageSources copia paquetes completos de código fuente al disco local incluso en un destino readOnly: agrégalo a deniedTools donde el código fuente no deba salir de SAP.
  • Qué sale de la máquina. El servidor habla con los hosts SAP configurados, con el proveedor de identidad durante el SSO del navegador y con GitHub para el repositorio de cloudification de SAP cuando apiReleaseState se ejecuta (un archivo JSON por edición desde raw.githubusercontent.com/SAP/abap-atc-cr-cv-s4hc, tiempo de espera de 15 segundos, almacenado en caché durante 24 horas bajo ~/.abap-adt-mcp/cache, reubicable con MCP_CACHE_DIR; se usa una copia en caché cuando la descarga falla). No hay interruptor sin conexión, URL de espejo ni soporte de proxy para esa descarga (usa el fetch integrado de Node, que ignora HTTPS_PROXY): en un host aislado siembra el directorio de caché una vez, o deja que esa única herramienta falle. Nada más se envía a ningún lugar: sin telemetría, sin comprobaciones de actualización. npx en sí contacta con el registro npm.

Registro de auditoría

Establece MCP_AUDIT_FILE=/var/log/abap-adt-mcp/audit.jsonl para añadir una línea JSON por llamada a herramienta. El directorio se crea con modo 0700 y el archivo con 0600; un fallo de escritura se reporta una vez en stderr y nunca interrumpe una llamada. Cada registro se añade por ruta, así que rotar el archivo renombrándolo es seguro (la siguiente llamada crea uno nuevo); el servidor no mantiene retención propia. docs/FIELD-NOTES.md explica cómo convertir el archivo en un informe de sesión útil.

{"ts":"2026-09-03T10:15:42.117Z","requestId":42,"tool":"editObjectSource","destination":"DEV","durationMs":1834,"outcome":"ok","args":{"objectSourceUrl":"/sap/bc/adt/oo/classes/zcl_example/source/main","replacements":"[array 312 chars]","activate":true,"transport":"DEVK900123"}}
{"ts":"2026-09-03T10:16:03.902Z","requestId":43,"tool":"runQuery","destination":"QAS","durationMs":2,"outcome":"denied","args":{"sqlQuery":"SELECT * FROM ztable"},"errorKind":"policyDenied","gate":"allowFreeSql","message":"MCP error -32600: Policy: runQuery blocked on destination QAS (allowFreeSql): free SQL (runQuery) is disabled; use tableContents on an allowed table. Configured in systems.json policy; retrying will not help."}

Campos: ts, requestId, tool, destination, durationMs, outcome (ok, error, denied para rechazos de política, unavailable para compuertas de conjunto de herramientas o plataforma), errorKind, gate (la clave de política), message (el texto de error, primeros 300 caracteres), args y retried (establecidos cuando la llamada fue re-autenticada y reintentada). Lo que args mantiene: claves de argumento que contienen pass (así que password y passphrase), secret, token, authorization, cookie o lockHandle se convierten en [REDACTED]; valores de cadena de hasta 200 caracteres se almacenan textualmente después de la misma redacción que los mensajes de error (así que una declaración SQL o un fragmento corto con literales comerciales está en el archivo), cadenas más largas se truncan, y arrays u objetos de más de 200 caracteres colapsan a [array N chars] o [object N chars]. Trata el archivo como sensible. No hay identidad de llamante en un registro (sin dirección remota, ID de sesión MCP o ID de token): en stdio el proceso pertenece a una persona, y en una instancia HTTP compartida la atribución tiene que venir de ejecutar una instancia por persona o del registro de acceso del proxy inverso al frente.

S/4HANA Cloud versus on-prem

systemProfile(destination) reporta si un destino es cloud u on-prem (dominio del host, información del sistema y documento de descubrimiento) y qué conjuntos de herramientas le faltan al backend; esas herramientas se rechazan antes de llamar a SAP. Si solo tienes un tenant de S/4HANA Cloud, la columna del medio es tuya. Lo que docs/TESTPLAN.md y docs/FIELD-NOTES.md registraron en un tenant de Public Cloud:

TemaS/4HANA Cloud (edición pública)On-prem / privado
AutenticaciónUsuarios nombrados: solo SSO del navegador. Sin supervisión: OAuth2 desde un Communication Arrangement, o autenticación básica con un Communication User.Autenticación básica; certificados de cliente a través de tls.
Objetos locales$TMP fue rechazado en el tenant probado (objeto de autorización S_ABPLNGVS: objetos en $TMP obtienen la versión de idioma Standard); usa un paquete de cliente con ABAP for Cloud Development y su transporte, resolveTransport lo selecciona. runSnippet necesita packageName, transport y responsible allí.$TMP disponible, sin necesidad de transporte; runSnippet por defecto a $TMP.
Conjuntos de herramientasGenerador RAP ausente en el tenant probado; depurador, trazas y abapGit dependen del tenant y las autorizaciones. dumps/dumpDetails son la ruta de causa raíz cuando falta el depurador. sourceTextSearch cae a grepPackage cuando el tenant responde "Source Search is not supported".Conjunto completo de colecciones ADT en una versión actual.
APIs liberadasapiReleaseState verifica nombres, una URL de objeto o un código fuente completo; variante ATC ABAP_CLOUD_DEVELOPMENT_DEFAULT. createObject necesita responsible.Opcional.
Datos comercialesrunQuery/tableContents respetan autorizaciones de visualización; tablas de denegación por política. runSnippet necesita S_DEVELOP, así que solo sistemas de desarrollo.Igual.

Lecciones que aplican en todas partes: declaraciones runQuery se envuelven al límite de 255 caracteres por línea de la vista previa de datos; tablas con dataMaintenance restringido se leen con tableContents; un bloqueo mantenido por una sesión de Eclipse abierta es ajeno y solo SM12 o esa sesión pueden liberarlo; escribir una clase de mensajes a través de setObjectSource reescribe toda la clase y restablece masterLanguage al idioma de inicio de sesión.

Referencia de configuración

Cada opción con su valor por defecto, las compuertas de política herramienta por herramienta, fragmentos de host y notas operativas están en docs/CONFIGURATION.md; esta sección es el resumen.

Fuentes de configuración, en orden de precedencia: SAP_SYSTEMS (JSON en línea), SAP_SYSTEMS_FILE, un systems.json junto a la instalación, luego las variables de sistema único heredadas (SAP_URL, SAP_CLIENT, SAP_USER, SAP_PASSWORD, SAP_LANGUAGE, SAP_TLS_INSECURE, SAP_OAUTH_TOKEN_URL, SAP_OAUTH_CLIENT_ID, SAP_OAUTH_CLIENT_SECRET, SAP_OAUTH_SCOPE, ver .env.example).

Claves por destino en systems.json: url, client, language, authType, default, user/password (básico), oauth (tokenUrl, clientId, clientSecret, scope), insecureTls, gitUser/gitPassword, policy y tls (ca, servername, cert + key, pfx + passphrase). Cualquier valor de cadena puede ser ${env:VAR}. Las claves que comienzan con _ se ignoran, así que las entradas _comment están bien. Toda la salida operativa (advertencias de inicio, mensajes de compuerta, la advertencia del archivo de auditoría) va a stderr, que los hosts MCP capturan en sus registros.

Cada variable declarada en server.json:

VariablePropósitoValor predeterminado / notas
SAP_SYSTEMS_FILERuta al archivo de destinosRecomendado; mantener modo 0600
SAP_SYSTEMSEl mismo mapa en líneaContiene credenciales, preferir el archivo
SAP_DEFAULT_DESTINATIONDestino utilizado cuando una llamada omite destinationO marcar una entrada "default": true
SAP_AUTH_TYPETipo de autenticación predeterminado para entradas sin uno, y el modo de la configuración heredada de un solo sistemasso; basic o oauth
MCP_TOOLSETSConjuntos de herramientas a publicar: preajuste all o focused, o una lista separada por comasall
MCP_DISABLED_TOOLSETSConjuntos de herramientas a ocultar, lista separada por comascore no se puede deshabilitar
MCP_READ_ONLY1 hace que cada destino sea de solo lectura, del lado del servidorDesactivado
MCP_MAX_RESPONSE_CHARSPresupuesto de caracteres de una respuesta de herramienta antes de paginar o truncar40000, mínimo 5000
MCP_PROFILE_GATEPuerta para conjuntos de herramientas que el destino no exponeenforce; warn solo registra, off deshabilita
MCP_SOURCE_CACHE_TTL_SECONDSVida útil de la caché de código fuente por sesión utilizada por syntaxCheckCode, grepPackage, cdsViewInfo, typeHierarchy, abapDocumentation y apiReleaseState(sourceUrl)300; 0 mantiene entradas hasta el cierre de sesión
MCP_EXPORT_ROOTDirectorio en el que exportPackageSources puede escribir~/.abap-adt-mcp/exports
MCP_AUDIT_FILERuta del registro de auditoría JSONLDesactivado si no se establece
SAP_ALLOW_REENTRANCE_TICKET1 habilita la herramienta reentranceTicketDeshabilitado
SAP_BROWSER_PATHSSO: ruta a un binario de Chromium, Chrome o EdgeDetectado automáticamente
SAP_BROWSER_PROFILE_DIRSSO: perfil de navegador persistente que contiene la sesión del proveedor de identidad~/.abap-adt-mcp/sso/<host>
MCP_HTTP_PORTServir Streamable HTTP en http://127.0.0.1:<port>/mcp con autenticación bearer en lugar de stdioSin establecer (stdio); acepta 1024 a 65535
MCP_HTTP_HOSTDirección de enlace del transporte HTTP127.0.0.1; 0.0.0.0 solo en contenedores
MCP_HTTP_TOKENToken bearer para el transporte HTTPGenerado en ~/.abap-adt-mcp/http-token
MCP_HTTP_MAX_SESSIONSMáximo de sesiones MCP concurrentes; solicitudes initialize adicionales reciben 50316
MCP_HTTP_MAX_BODY_BYTESCuerpo de solicitud más grande que acepta el transporte HTTP; cuerpos más grandes reciben 4134194304 (4 MB)
MCP_HTTP_SESSION_TTL_MINUTESMinutos de inactividad tras los cuales se cierra una sesión HTTP (y sus sesiones SAP y bloqueos)30
MCP_HTTP_ALLOWED_ORIGINSValores Origin permitidos separados por comas; * permite cualquieraOrígenes de loopback siempre permitidos en un enlace de loopback
MCP_HTTP_ALLOWED_HOSTSValores de cabecera Host permitidos separados por comas (protección contra rebinding de DNS)Hosts de loopback siempre permitidos en un enlace de loopback; cualquier host en un enlace no loopback
SAP_URLModo heredado de un solo sistema: URL base, por ejemplo https://host:44300
SAP_CLIENTModo heredado de un solo sistema: cliente, por ejemplo 100
SAP_LANGUAGEModo heredado de un solo sistema: idioma de inicio de sesión, por ejemplo EN
SAP_USERModo heredado de un solo sistema: usuario SAP
SAP_PASSWORDModo heredado de un solo sistema: contraseña SAPSecreto
SAP_TLS_INSECUREModo heredado de un solo sistema: 1 omite la verificación de certificados solo para ese sistemaSolo sandboxes
SAP_OAUTH_TOKEN_URLModo heredado de un solo sistema con SAP_AUTH_TYPE=oauth: endpoint de token
SAP_OAUTH_CLIENT_IDModo heredado de un solo sistema: ID de cliente OAuth2
SAP_OAUTH_CLIENT_SECRETModo heredado de un solo sistema: secreto de cliente OAuth2Secreto
SAP_OAUTH_SCOPEModo heredado de un solo sistema: alcance OAuth2 opcional

Leído en tiempo de ejecución pero no parte del manifiesto de registro: MCP_CACHE_DIR reubica la caché del repositorio de cloudification (predeterminado ~/.abap-adt-mcp/cache), y NODE_TLS_REJECT_UNAUTHORIZED=0 se elimina al inicio para que no pueda deshabilitar la verificación de certificados para todo el proceso.

Transporte HTTP (opcional)

Por defecto, el servidor habla stdio: un proceso por usuario, sin nada escuchando en la red. Para hosts que esperan un endpoint HTTP (Eclipse, otra máquina, un contenedor, una instancia compartida de equipo), inícielo con un puerto:

MCP_HTTP_PORT=2236 npx -y abap-adt-mcp

Escucha en http://127.0.0.1:2236/mcp (solo loopback a menos que MCP_HTTP_HOST indique lo contrario) y requiere Authorization: Bearer <token> en cada solicitud. El token se genera al inicio y se escribe en ~/.abap-adt-mcp/http-token (modo 0600); MCP_HTTP_TOKEN establece el suyo propio. Configuración del host:

{
  "mcpServers": {
    "abap-adt-mcp": {
      "type": "http",
      "url": "http://127.0.0.1:2236/mcp",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}

Lo que la puerta de entrada aplica:

  • Se rechazan puertos por debajo de 1024; el token bearer se compara en tiempo constante; GET /health es la única ruta no autenticada y responde con versión, número de sesiones, el límite de sesiones y tiempo de actividad (bloquéelo en el proxy si esa divulgación importa). Todo lo demás fuera de /mcp es 404.
  • Protección contra rebinding de DNS: en un enlace de loopback solo pasan valores de Host y Origin de loopback, extensibles con MCP_HTTP_ALLOWED_HOSTS y MCP_HTTP_ALLOWED_ORIGINS (* permite cualquiera). En un enlace no loopback, cada cabecera Host pasa (la verificación de Host solo protege enlaces de loopback), mientras que una cabecera Origin aún debe estar listada en MCP_HTTP_ALLOWED_ORIGINS (llamadas de navegador); solicitudes sin cabecera Origin (clientes no navegador) pasan en cualquier enlace.
  • Una instancia de servidor por sesión MCP: sesiones SAP separadas, libro de bloqueos y cachés por llamador. Las sesiones inactivas expiran después de MCP_HTTP_SESSION_TTL_MINUTES (predeterminado 30); más allá de MCP_HTTP_MAX_SESSIONS (predeterminado 16), nuevas solicitudes initialize reciben 503 con Retry-After; una sesión cerrada o expirada libera sus bloqueos y sesiones SAP. En SIGINT/SIGTERM, el proceso cierra la instancia de escucha y sale sin recorrer las sesiones abiertas, así que envíe DELETE /mcp desde los clientes antes de detener una instancia compartida. Cada cuerpo de solicitud está limitado a MCP_HTTP_MAX_BODY_BYTES (predeterminado 4 MB); cuerpos más grandes se rechazan con 413 y la conexión se cierra.
  • Advertencias al inicio cuando el enlace va más allá de loopback, y nuevamente cuando un destino SSO se expone de esa manera: cada llamador remoto compartiría el inicio de sesión del navegador del usuario que ejecuta el servidor.

Lo que no proporciona: TLS (coloque un proxy inverso delante), limitación de velocidad, tokens por usuario o rotación de tokens sin reinicio (un reinicio sin MCP_HTTP_TOKEN ya genera un nuevo token y sobrescribe http-token; cuando establece la variable usted mismo, cámbiela y reinicie; las sesiones abiertas terminan con el proceso). Una instancia compartida significa, por lo tanto, un token y, para cada destino, un conjunto de credenciales SAP para cada llamador. Prefiera una instancia por persona, o destinos basic/oauth con una política readOnly, mantenga el token en secreto y coloque TLS delante.

Catálogo de herramientas (las 173 herramientas, por conjunto de herramientas)

La referencia por herramienta (descripción, parámetros, anotaciones de solo lectura/destructivas) está en docs/TOOLS.md, generada desde la respuesta tools/list en vivo por npm run tools:docs y verificada por una prueba de contrato en CI. Cada herramienta excepto listSystems y healthcheck acepta un destination opcional; sin él, se usa el destino predeterminado.

Los esquemas de herramientas cuestan contexto. Establezca MCP_TOOLSETS a un preajuste (all, el predeterminado, o focused = 114 herramientas de desarrollo) o a una lista separada por comas de los nombres a continuación; MCP_DISABLED_TOOLSETS elimina algunos. core siempre se publica. Nombres desconocidos fallan al inicio.

Conjunto de herramientasEn focusedHerramientas
core · Destinos, salud y sesión (6)login, logout, dropSession, listSystems, healthcheck, systemProfile
source · Código fuente (16)lock, unLock, listLocks, forceUnlock, getObjectSource, setObjectSource, editObjectSource, getMethodSource, setMethodSource, prettyPrinterSetting, setPrettyPrinterSetting, prettyPrinter, revisions, objectDiff, getTextElements, setTextElements
objects · Objetos y navegación (27)objectStructure, searchObject, findObjectPath, objectTypes, reentranceTicket, classIncludes, classComponents, deleteObject, activateObjects, activateByName, activatePackage, inactiveObjects, objectRegistrationInfo, creatableTypeDetails, validateNewObject, createObject, nodeContents, mainPrograms, typeHierarchy, objectStructureElements, objectEnhancements, packageTree, exportPackageSources, whereUsed, cdsViewInfo, sourceTextSearch, grepPackage
transports · Transportes (18)transportDetails, transportUnifiedDiff, transportInfo, resolveTransport, createTransport, hasTransportConfig, transportConfigurations, getTransportConfiguration, setTransportsConfig, createTransportsConfig, userTransports, transportsByConfig, transportDelete, transportRelease, transportSetOwner, transportAddUser, systemUsers, transportReference
analysis · Sintaxis y análisis de código (16)syntaxCheckCode, syntaxCheckCdsUrl, codeCompletion, findDefinition, usageReferences, syntaxCheckTypes, codeCompletionFull, runClass, codeCompletionElement, usageReferenceSnippets, fixProposals, fixEdits, fragmentMappings, abapDocumentation, apiReleaseState, runSnippet
tests · Pruebas unitarias (4)unitTestRun, unitTestEvaluation, unitTestOccurrenceMarkers, createTestInclude
atc · ATC (14)atcCustomizing, atcQuickfixProposals, atcApplyQuickfix, atcCheckVariant, atcSummary, createAtcRun, atcWorklists, atcUsers, atcExemptProposal, atcRequestExemption, isProposalMessage, atcContactUri, atcChangeContact, atcDocumentation
data · Acceso a datos y DDIC (10)annotationDefinitions, ddicElement, ddicRepositoryAccess, packageSearchHelp, getDomainProperties, setDomainProperties, getDataElementProperties, setDataElementProperties, tableContents, runQuery
discovery · Descubrimiento y metadatos (7)nofeatureDetails, collectionFeatureDetails, findCollectionByUrl, loadTypes, adtDiscovery, adtCoreDiscovery, adtCompatibilityGraph
runtime · Errores de ejecución (3)feeds, dumps, dumpDetails
refactoring · Refactorización (8)norenameEvaluate, renamePreview, renameExecute, extractMethodEvaluate, extractMethodPreview, extractMethodExecute, changePackagePreview, changePackageExecute
rap · Generación RAP (8)norapGenIsAvailable, rapGenGetSchema, rapGenGetContent, rapGenValidateInitial, rapGenValidateContent, rapGenPreview, rapGenGenerate, rapGenPublishService
services · Servicios empresariales (4)nopublishServiceBinding, unPublishServiceBinding, fetchServiceDetails, bindingDetails
git · abapGit (10)nogitRepos, gitExternalRepoInfo, gitCreateRepo, gitPullRepo, gitUnlinkRepo, stageRepo, pushRepo, checkRepo, remoteRepoInfo, switchRepoBranch
debugger · Depurador (13)nodebuggerListeners, debuggerListen, debuggerDeleteListener, debuggerSetBreakpoints, debuggerDeleteBreakpoints, debuggerAttach, debuggerSaveSettings, debuggerStackTrace, debuggerVariables, debuggerChildVariables, debuggerStep, debuggerGoToStack, debuggerSetVariableValue
traces · Trazas (9)notracesList, tracesListRequests, tracesHitList, tracesDbAccess, tracesStatements, tracesSetParameters, tracesCreateConfiguration, tracesDeleteConfiguration, tracesDelete
Herramientas destructivas (deleteObject, transportRelease, transportDelete, setObjectSource, editObjectSource, setMethodSource, atcApplyQuickfix, runClass, runSnippet, pushRepo, forceUnlock y otras) llevan destructiveHint: true para hosts que controlan la aprobación mediante anotaciones. Una herramienta puede faltar por dos razones: su conjunto de herramientas no está publicado (el preset focused omite debugger, traces, git, rap, services, refactoring y discovery; la denegación nombra el conjunto de herramientas), o el destino no puede servirla (systemProfile informa qué falta; la denegación dice "no disponible en el destino").

Comparación con el servidor MCP ADT oficial de SAP

El servidor MCP ADT de SAP se distribuye con ADT para VS Code y Eclipse y se publica bajo la clave de servidor abap-adt con sus propios nombres de herramientas, a los que las habilidades públicas como claude-abap-skills enrutan. Este proyecto se publica bajo abap-adt-mcp, sirve muchos destinos desde un solo proceso a través de stdio o HTTP, aplica políticas en el lado del servidor y añade composiciones como resolveTransport, editObjectSource, grepPackage, apiReleaseState, runSnippet y objectDiff. Ambos pueden registrarse lado a lado en el mismo host, ya que las claves y los nombres de herramientas no colisionan. Este README no cataloga lo que el servidor de SAP ofrece más allá de este; docs/ROUTING.md mapea los nombres de SAP a los nuestros donde existe un equivalente. Algunas filas:

Herramienta/capacidad oficial de SAPHerramienta(s) de abap-adt-mcp
abap_lists_destinationslistSystems, systemProfile
SAPRead / abap_get_sourcegetObjectSource (version=inactive para código no activado)
SAPSearch / abap_search_objectssearchObject; por contenido sourceTextSearch, grepPackage
abap_write_source / SAPWritesetObjectSource (activate=true), editObjectSource dirigido
abap_activate_objects / ActivatePackageactivateByName, activateObjects, inactiveObjects
abap_run_unit_testsunitTestRun, unitTestEvaluation
abap_atc_run / abap_atc_findingscreateAtcRun, atcWorklists, atcQuickfixProposals, atcApplyQuickfix, atcDocumentation
abap_transport-unifiedDifferencetransportUnifiedDiff, transportDetails
abap_generators-*rapGenIsAvailable, rapGenGetSchema, rapGenValidateContent, rapGenPreview, rapGenGenerate, rapGenPublishService
abap_lock / abap_unlockNo necesario para escrituras individuales (bloqueo automático); lock, unLock, listLocks, forceUnlock
abap_dumpsdumps, dumpDetails
verificación de API liberada / Clean CoreapiReleaseState

Habilidades y plugin

Dos habilidades de agente se distribuyen bajo skills/: abap-adt-mcp enseña al modelo cómo desarrollar ABAP con estas herramientas (inicio de sesión, búsqueda de código, flujo de cambios, preparación para la nube, errores, seguridad) y abap-adt-mcp-setup guía a través de la instalación, configuración y una primera verificación de salud. Llegan al host a través del plugin de Claude Code (/plugin marketplace add williansaez/abap-adt-mcp, luego /plugin install abap-adt-mcp@abap-adt-mcp, que también registra el servidor), a través del instalador de terceros npx skills add williansaez/abap-adt-mcp, o copiando los dos directorios en ~/.claude/skills/; un registro simple de npx del servidor no instala ninguna habilidad, y los flujos esenciales aún llegan a través del campo instructions del servidor y los prompts integrados.

Este README también existe en Portugués (Brasil) y Alemán; la versión en inglés es la referencia y los recuentos generados se sincronizan en los tres. Lo que las sesiones reales enseñaron al servidor está en docs/FIELD-NOTES.md, el plan de pruebas en vivo en docs/TESTPLAN.md, la hoja de ruta en docs/ROADMAP.md y los lanzamientos en CHANGELOG.md.

Solución de problemas

  • El servidor nunca aparece en el host. Lea el registro MCP del host (ubicaciones en paso 2). spawn npx ENOENT: Node.js no está instalado o no está en el PATH que la aplicación ve; instálelo o ponga la ruta absoluta a npx en command (/usr/local/bin/npx para el instalador de macOS, /opt/homebrew/bin/npx para Homebrew). EBADENGINE en el registro: el Node que el host encontró es anterior a 22.12; instale el LTS actual. No ABAP systems configured: SAP_SYSTEMS_FILE apunta a un archivo faltante. is not valid JSON: una coma suelta o una ruta de Windows con barras invertidas simples. Claude Desktop lee la configuración solo al inicio, así que ciérrelo y vuelva a abrirlo después de cada cambio.
  • Sin ventana del navegador, o SSO falla. Debe estar instalado un navegador Chromium; SAP_BROWSER_PATH apunta a él cuando falla la detección automática. El perfil predeterminado del navegador se rechaza a propósito; SAP_BROWSER_PROFILE_DIR nombra uno dedicado. Elimine ~/.abap-adt-mcp/sso/<host> para cerrar sesión de un tenant por completo.
  • El inicio de sesión funciona, luego todo es "no autorizado" o "no encontrado". La sesión SSO aterrizó en otro cliente del que dice client: establezca client al cliente de inicio de sesión del tenant (la entrada Acerca de del menú de usuario del launchpad lo muestra).
  • kind: "sessionExpired" sigue apareciendo. El servidor ya se re-autenticó y reintentó una vez; pida al modelo que llame a login para ese destino. Los identificadores de bloqueo de la sesión anterior son inválidos (kind: "staleLockHandle"): bloquee de nuevo.
  • kind: "locked" por otra sesión. listLocks muestra los bloqueos propios del servidor; si el objeto no está allí, el bloqueo pertenece a otra sesión (Eclipse u otro usuario) y solo esa sesión o SM12 lo libera.
  • editObjectSource informa 0 coincidencias, o varias. No se escribió nada. El ancla debe ser el texto actual exacto en SAP, incluida la indentación: vuelva a leer con getObjectSource y cópielo; para varias coincidencias incluya más líneas circundantes.
  • Herramienta denegada como no disponible o no habilitada. "No disponible en el destino": ejecute systemProfile, el tenant carece de esa colección ADT (MCP_PROFILE_GATE=warn solo registra, off desactiva la puerta). "Pertenece al conjunto de herramientas ... que no está habilitado": el conjunto de herramientas falta en MCP_TOOLSETS (el preset focused no tiene debugger o traces); añádalo o use MCP_TOOLSETS=all. Sin un depurador, dumps y dumpDetails son la ruta de causa raíz.
  • kind: "policyDenied". El policy del destino (o MCP_READ_ONLY) prohíbe la llamada y el mensaje nombra la puerta: la barrera de protección está funcionando. Ajuste la política si la llamada era intencionada.
  • El inicio rechaza el archivo de configuración. Es legible por otros usuarios y contiene contraseñas en línea: chmod 600 o haga referencia a los secretos como ${env:VAR}.
  • Errores de certificado en las instalaciones (kind: "tlsCertificate"). La pista nombra el destino y la solución. Emisor desconocido: dé al destino su paquete de CA con tls.ca (la pista lleva la línea openssl s_client). Desajuste de nombre (el sistema se alcanza por dirección IP o nombre de host corto): establezca tls.servername al nombre DNS: que la cita del mensaje. Expirado: solo la renovación en STRUST lo soluciona. insecureTls: true (o SAP_TLS_INSECURE=1 en modo heredado) desactiva la verificación solo para ese destino. NODE_TLS_REJECT_UNAUTHORIZED=0 no ayudará: el servidor lo elimina.
  • Errores de conexión. Verifique URL y cliente, autorizaciones ADT, y en las instalaciones que /sap/bc/adt esté activo en SICF.
  • runQuery falla en una tabla que el usuario puede mostrar. La vista previa de datos rechaza tablas con dataMaintenance restringido; use tableContents. Una declaración que aún falla después del reflujo de 255 caracteres tiene un literal único más largo que eso, o un error de sintaxis real en el token nombrado.
  • Los esquemas de herramientas consumen la ventana de contexto. Comience con MCP_TOOLSETS=focused, u oculte conjuntos de herramientas (MCP_DISABLED_TOOLSETS=debugger,traces).

Pruebas y contribuciones

git clone https://github.com/williansaez/abap-adt-mcp.git
cd abap-adt-mcp
npm ci
npm run build
npm test

Las suites de Jest cubren manejadores, pistas de error, tamaño de respuestas, conjuntos de herramientas y el contrato de catálogo contra docs/tools.snapshot.json; CI las ejecuta en Node 22 y 24, construye la imagen del contenedor y verifica que se inicia y lista herramientas. Después de cambiar una descripción o esquema de herramienta, ejecute npm run tools:docs y confirme el docs/TOOLS.md regenerado, la instantánea y los recuentos del README (incluidos los README traducidos), o CI los marca como obsoletos; npm run docs:check ejecuta la puerta de higiene de documentación (sin identificadores de cliente, sin guiones largos, sin enlaces muertos, cada variable de entorno declarada en server.json). Los lanzamientos están impulsados por etiquetas: npm a través de publicación confiable (GitHub OIDC, procedencia adjunta) más la imagen GHCR. Haga un fork, cree una rama, abra una solicitud de extracción. Los informes de sesión para docs/FIELD-NOTES.md son bienvenidos, sin nombres de clientes, tenants o números de transporte.

Licencia

MIT. Construido sobre abap-adt-api por Marcello Urbani. Si el proyecto le ahorra tiempo, puede patrocinar al autor.