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.
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
policyes 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
- Configuración
- Qué pedirle al modelo
- Flujos de trabajo en detalle
- Prompts integrados
- Otras formas de instalar
- Autenticación
- Mantenerlo seguro
- Registro de auditoría
- S/4HANA Cloud frente a on-prem
- Referencia de configuración
- Transporte HTTP (opcional)
- Catálogo de herramientas (las 173 herramientas, por conjunto)
- Comparación con el servidor MCP ADT oficial de SAP
- Skills y plugin
- Solución de problemas
- Pruebas y contribuciones
- Licencia
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,
npmimprimeEBADENGINEy el servidor no está probado; instala el LTS actual y reinicia el host. La imagen de contenedor ya estaba ennode:22-alpine. tls.servernameen 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 yinsecureTlsya no es la única vía para ese paisaje.listSystemsmuestraservername 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íneaopenssl s_clientpara ese host y apunta atls.ca; una discrepancia de nombre cita los nombres que Node informó y apunta atls.servername; un certificado caducado dice que solo la renovación lo soluciona.insecureTlsse menciona al final. insecureTlspermanece, por destino, desactivado por defecto, anunciado al inicio; SECURITY.md documenta por qué.- Cadena de suministro.
puppeteer-core25 elimina la última alerta abierta de Dependabot del árbol de dependencias (npm auditinforma 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;dotenvse 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
npmynpx, que es todo lo que necesita el host. No se requiere terminal para comprobarlo: si Node falta, el registro del host dicespawn npx ENOENTcuando 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_DEVELOPERen la entrega estándar); si Eclipse ADT funciona para ti, este servidor también funciona. On-prem, el servicio/sap/bc/adtdebe estar activo en la transacciónSICF(una tarea de Basis) y tu usuario necesita las autorizaciones habituales de desarrollo ADT. Solo los clientesoauthdesatendidos 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:
| Pregunta | Herramientas 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:
resolveTransport(objSourceUrl)devuelve el transporte que ya registra el objeto, el más reciente modificable para su paquete, oneedsTransport: falsepara paquetes locales;createIfMissing=truecrea uno cuando no existe ninguno.syntaxCheckCodesobre la fuente prevista: opcional para un cambio de una línea, seguro barato para algo más grande.editObjectSource(objectSourceUrl, replacements=[{oldText, newText}], activate=true, transport)para cambios específicos (el servidor relee SAP primero; cadaoldTextdebe 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 bloqueMETHOD ... ENDMETHODen la implementación (pasa el bloque completo o solo el cuerpo; la parte de definición permanece como está;includeyclassNameseleccionan clases locales o de prueba; un método desconocido se rechaza con la lista de métodos presentes),setObjectSourcepara reescrituras completas.- Lee el campo
activationdel resultado; corrige y escribe de nuevo, oactivateByName/activatePackagemás tarde. unitTestRun(url), luegoobjectDiff(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:
| Prompt | Argumentos | Qué hace | Dónde se detiene |
|---|---|---|---|
create-object | opcional destination, luego objectType (ID de tipo ADT como CLAS/OC, INTF/OI, PROG/P, DDLS/DF), name, package, opcional purpose | Validar, 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-edit | opcional destination, luego object (nombre o URL), change | Leer, 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-transport | opcional 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-atc | opcional destination, luego target (URL de objeto, nombre de paquete o transporte), opcional variant | Ejecutar 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-check | opcional 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-dump | opcional 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 para | Qué configura | Configuració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 |
basic | AS ABAP local, Usuarios de Comunicación de S/4HANA Cloud | user y password (use ${env:VAR}). Autentica en la primera llamada, login es opcional. | Un usuario con autorizaciones de ADT |
oauth | Clientes desatendidos de S/4HANA Cloud | oauth.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
policyde un destino se evalúa en el servidor antes de la llamada SAP propia de la herramienta, lo que sea que el host apruebe;allowedPackageses la única puerta que puede necesitar una búsqueda (transportInfo, en caché) para aprender el paquete de un objeto existente primero. Los rechazos vuelven comokind: "policyDenied"nombrando la puerta, ylistSystemsmuestra cada política. Un destino sin bloquepolicyes completamente escribible.Clave Tipo Efecto readOnlybooleano Solo las herramientas anotadas como solo lectura pueden ejecutarse, más login,logout,dropSession,listSystems,healthcheck,systemProfileyexportPackageSources(que escribe solo localmente). Bloqueadas como escrituras: cada escritura de fuente,lock,runSnippet,runClass,unitTestRun,createAtcRunyatcSummary. Aún permitidas:runQueryytableContents(son lecturas; niéguelas conallowFreeSql: falseodeniedTools).deniedToolsglobs Herramientas rechazadas por completo en este destino: un nombre, un glob ( rapGen*) otoolset:<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í quegit*solo deja abierta la ruta de push. Las herramientas permanecen listadas.allowFreeSqlbooleano falserechazarunQueryytableContentsconsqlQuery.deniedTablesglobs Aplicado a tableContents, a cada objetivoFROM/JOINde unrunQuery, y (mejor esfuerzo, escaneando el texto ABAP) arunSnippet,setObjectSourceysetMethodSource. 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 combineallowFreeSql: falsecondeniedTools: ["runSnippet"]oreadOnly.allowedPackagesglobs, lista cerrada Bloquea 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,publishServiceBindingyunPublishServiceBindingno pueden derivar un paquete y se rechazan siempre que esta clave esté establecida.allowedTransportsglobs Cada argumento transport/transportNumberdebe coincidir;createTransportyresolveTransport(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 desystems.json(password,oauth.clientSecret,gitPassword,tls.passphrase, inclusourl); una variable faltante falla al inicio por nombre, nunca por valor. Manténsystems.jsonen modo0600: un archivo legible por grupo o por todos se advierte y se rechaza cuando contiene unpassword,oauth.clientSecretogitPassworden línea. PrefiereSAP_SYSTEMS_FILEsobreSAP_SYSTEMSen línea en las configuraciones del host.MCP_HTTP_TOKENes 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 en0600también.listSystemsyhealthcheckno reportan credenciales, los mensajes de error pasan por un paso de redacción que enmascara tokens de portador, cookies, contraseñas y URLs deuser:password@host, yexportPackageSourcessolo puede escribir dentro deMCP_EXPORT_ROOT(por defecto~/.abap-adt-mcp/exports, verificado contra enlaces simbólicos).reentranceTicketpermanece deshabilitado a menos queSAP_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=0se 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 usatls.caen ese destino, para un certificado emitido a un nombre distinto del que está enurlusatls.servername(la verificación permanece activada en ambos casos, sin advertencia), o como último recursoinsecureTls: truesolo en ese destino (anunciado al inicio, mostrado porlistSystems). Un handshake fallido regresa comokind: "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.
runQueryytableContentsleen datos comerciales reales, así que configura solo destinos donde eso sea aceptable, yexportPackageSourcescopia paquetes completos de código fuente al disco local incluso en un destinoreadOnly: agrégalo adeniedToolsdonde 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
apiReleaseStatese ejecuta (un archivo JSON por edición desderaw.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 conMCP_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 elfetchintegrado de Node, que ignoraHTTPS_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.npxen 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:
| Tema | S/4HANA Cloud (edición pública) | On-prem / privado |
|---|---|---|
| Autenticación | Usuarios 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 herramientas | Generador 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 liberadas | apiReleaseState verifica nombres, una URL de objeto o un código fuente completo; variante ATC ABAP_CLOUD_DEVELOPMENT_DEFAULT. createObject necesita responsible. | Opcional. |
| Datos comerciales | runQuery/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:
| Variable | Propósito | Valor predeterminado / notas |
|---|---|---|
SAP_SYSTEMS_FILE | Ruta al archivo de destinos | Recomendado; mantener modo 0600 |
SAP_SYSTEMS | El mismo mapa en línea | Contiene credenciales, preferir el archivo |
SAP_DEFAULT_DESTINATION | Destino utilizado cuando una llamada omite destination | O marcar una entrada "default": true |
SAP_AUTH_TYPE | Tipo de autenticación predeterminado para entradas sin uno, y el modo de la configuración heredada de un solo sistema | sso; basic o oauth |
MCP_TOOLSETS | Conjuntos de herramientas a publicar: preajuste all o focused, o una lista separada por comas | all |
MCP_DISABLED_TOOLSETS | Conjuntos de herramientas a ocultar, lista separada por comas | core no se puede deshabilitar |
MCP_READ_ONLY | 1 hace que cada destino sea de solo lectura, del lado del servidor | Desactivado |
MCP_MAX_RESPONSE_CHARS | Presupuesto de caracteres de una respuesta de herramienta antes de paginar o truncar | 40000, mínimo 5000 |
MCP_PROFILE_GATE | Puerta para conjuntos de herramientas que el destino no expone | enforce; warn solo registra, off deshabilita |
MCP_SOURCE_CACHE_TTL_SECONDS | Vida ú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_ROOT | Directorio en el que exportPackageSources puede escribir | ~/.abap-adt-mcp/exports |
MCP_AUDIT_FILE | Ruta del registro de auditoría JSONL | Desactivado si no se establece |
SAP_ALLOW_REENTRANCE_TICKET | 1 habilita la herramienta reentranceTicket | Deshabilitado |
SAP_BROWSER_PATH | SSO: ruta a un binario de Chromium, Chrome o Edge | Detectado automáticamente |
SAP_BROWSER_PROFILE_DIR | SSO: perfil de navegador persistente que contiene la sesión del proveedor de identidad | ~/.abap-adt-mcp/sso/<host> |
MCP_HTTP_PORT | Servir Streamable HTTP en http://127.0.0.1:<port>/mcp con autenticación bearer en lugar de stdio | Sin establecer (stdio); acepta 1024 a 65535 |
MCP_HTTP_HOST | Dirección de enlace del transporte HTTP | 127.0.0.1; 0.0.0.0 solo en contenedores |
MCP_HTTP_TOKEN | Token bearer para el transporte HTTP | Generado en ~/.abap-adt-mcp/http-token |
MCP_HTTP_MAX_SESSIONS | Máximo de sesiones MCP concurrentes; solicitudes initialize adicionales reciben 503 | 16 |
MCP_HTTP_MAX_BODY_BYTES | Cuerpo de solicitud más grande que acepta el transporte HTTP; cuerpos más grandes reciben 413 | 4194304 (4 MB) |
MCP_HTTP_SESSION_TTL_MINUTES | Minutos de inactividad tras los cuales se cierra una sesión HTTP (y sus sesiones SAP y bloqueos) | 30 |
MCP_HTTP_ALLOWED_ORIGINS | Valores Origin permitidos separados por comas; * permite cualquiera | Orígenes de loopback siempre permitidos en un enlace de loopback |
MCP_HTTP_ALLOWED_HOSTS | Valores 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_URL | Modo heredado de un solo sistema: URL base, por ejemplo https://host:44300 | |
SAP_CLIENT | Modo heredado de un solo sistema: cliente, por ejemplo 100 | |
SAP_LANGUAGE | Modo heredado de un solo sistema: idioma de inicio de sesión, por ejemplo EN | |
SAP_USER | Modo heredado de un solo sistema: usuario SAP | |
SAP_PASSWORD | Modo heredado de un solo sistema: contraseña SAP | Secreto |
SAP_TLS_INSECURE | Modo heredado de un solo sistema: 1 omite la verificación de certificados solo para ese sistema | Solo sandboxes |
SAP_OAUTH_TOKEN_URL | Modo heredado de un solo sistema con SAP_AUTH_TYPE=oauth: endpoint de token | |
SAP_OAUTH_CLIENT_ID | Modo heredado de un solo sistema: ID de cliente OAuth2 | |
SAP_OAUTH_CLIENT_SECRET | Modo heredado de un solo sistema: secreto de cliente OAuth2 | Secreto |
SAP_OAUTH_SCOPE | Modo 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 /healthes 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/mcpes404. - Protección contra rebinding de DNS: en un enlace de loopback solo pasan valores de
HostyOriginde loopback, extensibles conMCP_HTTP_ALLOWED_HOSTSyMCP_HTTP_ALLOWED_ORIGINS(*permite cualquiera). En un enlace no loopback, cada cabeceraHostpasa (la verificación de Host solo protege enlaces de loopback), mientras que una cabeceraOriginaún debe estar listada enMCP_HTTP_ALLOWED_ORIGINS(llamadas de navegador); solicitudes sin cabeceraOrigin(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á deMCP_HTTP_MAX_SESSIONS(predeterminado 16), nuevas solicitudesinitializereciben503conRetry-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íeDELETE /mcpdesde los clientes antes de detener una instancia compartida. Cada cuerpo de solicitud está limitado aMCP_HTTP_MAX_BODY_BYTES(predeterminado 4 MB); cuerpos más grandes se rechazan con413y 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 herramientas | En focused | Herramientas |
|---|---|---|
core · Destinos, salud y sesión (6) | sí | login, logout, dropSession, listSystems, healthcheck, systemProfile |
source · Código fuente (16) | sí | lock, unLock, listLocks, forceUnlock, getObjectSource, setObjectSource, editObjectSource, getMethodSource, setMethodSource, prettyPrinterSetting, setPrettyPrinterSetting, prettyPrinter, revisions, objectDiff, getTextElements, setTextElements |
objects · Objetos y navegación (27) | sí | 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) | sí | 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) | sí | syntaxCheckCode, syntaxCheckCdsUrl, codeCompletion, findDefinition, usageReferences, syntaxCheckTypes, codeCompletionFull, runClass, codeCompletionElement, usageReferenceSnippets, fixProposals, fixEdits, fragmentMappings, abapDocumentation, apiReleaseState, runSnippet |
tests · Pruebas unitarias (4) | sí | unitTestRun, unitTestEvaluation, unitTestOccurrenceMarkers, createTestInclude |
atc · ATC (14) | sí | atcCustomizing, atcQuickfixProposals, atcApplyQuickfix, atcCheckVariant, atcSummary, createAtcRun, atcWorklists, atcUsers, atcExemptProposal, atcRequestExemption, isProposalMessage, atcContactUri, atcChangeContact, atcDocumentation |
data · Acceso a datos y DDIC (10) | sí | annotationDefinitions, ddicElement, ddicRepositoryAccess, packageSearchHelp, getDomainProperties, setDomainProperties, getDataElementProperties, setDataElementProperties, tableContents, runQuery |
discovery · Descubrimiento y metadatos (7) | no | featureDetails, collectionFeatureDetails, findCollectionByUrl, loadTypes, adtDiscovery, adtCoreDiscovery, adtCompatibilityGraph |
runtime · Errores de ejecución (3) | sí | feeds, dumps, dumpDetails |
refactoring · Refactorización (8) | no | renameEvaluate, renamePreview, renameExecute, extractMethodEvaluate, extractMethodPreview, extractMethodExecute, changePackagePreview, changePackageExecute |
rap · Generación RAP (8) | no | rapGenIsAvailable, rapGenGetSchema, rapGenGetContent, rapGenValidateInitial, rapGenValidateContent, rapGenPreview, rapGenGenerate, rapGenPublishService |
services · Servicios empresariales (4) | no | publishServiceBinding, unPublishServiceBinding, fetchServiceDetails, bindingDetails |
git · abapGit (10) | no | gitRepos, gitExternalRepoInfo, gitCreateRepo, gitPullRepo, gitUnlinkRepo, stageRepo, pushRepo, checkRepo, remoteRepoInfo, switchRepoBranch |
debugger · Depurador (13) | no | debuggerListeners, debuggerListen, debuggerDeleteListener, debuggerSetBreakpoints, debuggerDeleteBreakpoints, debuggerAttach, debuggerSaveSettings, debuggerStackTrace, debuggerVariables, debuggerChildVariables, debuggerStep, debuggerGoToStack, debuggerSetVariableValue |
traces · Trazas (9) | no | tracesList, 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 SAP | Herramienta(s) de abap-adt-mcp |
|---|---|
abap_lists_destinations | listSystems, systemProfile |
SAPRead / abap_get_source | getObjectSource (version=inactive para código no activado) |
SAPSearch / abap_search_objects | searchObject; por contenido sourceTextSearch, grepPackage |
abap_write_source / SAPWrite | setObjectSource (activate=true), editObjectSource dirigido |
abap_activate_objects / ActivatePackage | activateByName, activateObjects, inactiveObjects |
abap_run_unit_tests | unitTestRun, unitTestEvaluation |
abap_atc_run / abap_atc_findings | createAtcRun, atcWorklists, atcQuickfixProposals, atcApplyQuickfix, atcDocumentation |
abap_transport-unifiedDifference | transportUnifiedDiff, transportDetails |
abap_generators-* | rapGenIsAvailable, rapGenGetSchema, rapGenValidateContent, rapGenPreview, rapGenGenerate, rapGenPublishService |
abap_lock / abap_unlock | No necesario para escrituras individuales (bloqueo automático); lock, unLock, listLocks, forceUnlock |
abap_dumps | dumps, dumpDetails |
| verificación de API liberada / Clean Core | apiReleaseState |
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 anpxencommand(/usr/local/bin/npxpara el instalador de macOS,/opt/homebrew/bin/npxpara Homebrew).EBADENGINEen el registro: el Node que el host encontró es anterior a 22.12; instale el LTS actual.No ABAP systems configured:SAP_SYSTEMS_FILEapunta 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_PATHapunta a él cuando falla la detección automática. El perfil predeterminado del navegador se rechaza a propósito;SAP_BROWSER_PROFILE_DIRnombra 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: establezcacliental 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 aloginpara 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.listLocksmuestra 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 oSM12lo libera.editObjectSourceinforma 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 congetObjectSourcey 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=warnsolo registra,offdesactiva la puerta). "Pertenece al conjunto de herramientas ... que no está habilitado": el conjunto de herramientas falta enMCP_TOOLSETS(el presetfocusedno tienedebuggerotraces); añádalo o useMCP_TOOLSETS=all. Sin un depurador,dumpsydumpDetailsson la ruta de causa raíz. kind: "policyDenied". Elpolicydel destino (oMCP_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 600o 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 contls.ca(la pista lleva la líneaopenssl s_client). Desajuste de nombre (el sistema se alcanza por dirección IP o nombre de host corto): establezcatls.servernameal nombreDNS:que la cita del mensaje. Expirado: solo la renovación enSTRUSTlo soluciona.insecureTls: true(oSAP_TLS_INSECURE=1en modo heredado) desactiva la verificación solo para ese destino.NODE_TLS_REJECT_UNAUTHORIZED=0no ayudará: el servidor lo elimina. - Errores de conexión. Verifique URL y cliente, autorizaciones ADT, y en las instalaciones que
/sap/bc/adtesté activo enSICF. runQueryfalla en una tabla que el usuario puede mostrar. La vista previa de datos rechaza tablas condataMaintenancerestringido; usetableContents. 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.