Ciltress/sap-abap-mcp
SAP ABAP MCP - ADT e JSON RPC
Documentação
Servidor SAP ABAP MCP (ADT e JSON RPC)
Um servidor MCP que dá a um agente de IA acesso total de leitura/escrita a um sistema SAP ABAP por meio de ADT (ABAP Development Tools), autenticado com SPNEGO/Kerberos single sign-on, um certificado de cliente X.509 ou um token portador OAuth 2.0 — sem senha em nenhum lugar da configuração. Usuário e senha ainda estão disponíveis como fallback, mas não são recomendados!
Ele encapsula abap-adt-api e adiciona uma coisa que o ADT sozinho não pode fazer: chamar módulos de função habilitados para RFC, por meio do serviço JSON-RPC 2.0 do SAP Gateway.
128 ferramentas — CRUD de objetos, edição de código-fonte, locks, transports, ativação, verificações de sintaxe, code completion, ABAP Unit, ATC, DDIC, abapGit, refatoração, traces, o depurador e chamadas RFC.
Quantas um cliente realmente vê é menor, duas vezes. Perfis (ABAP_MCP_PROFILE) trocam superfície por contexto: core lista 9 ferramentas, enquanto all lista 129, o que é ~2.700 tokens contra ~17.800 a cada turno para um cliente que não pode buscar esquemas de ferramentas sob demanda. E o servidor pergunta ao sistema o que ele suporta antes de listar qualquer coisa, então uma versão sem o plugin abapGit nunca recebe as 10 ferramentas que responderiam 400. Em DEV, isso deixa 116.
Este é o fork SSO de
mcp-abap-abap-adt-apipor mario-andreschak. As principais diferenças: SSO Kerberos em vez de Basic Auth, as ferramentas JSON-RPC/RFC, um roteador derivado das definições de ferramentas e uma referência de ferramentas documentada.
Documentação
| Documento | O que cobre |
|---|---|
AGENTS.md | Trabalhar neste repositório: layout, convenções, como testar. Leia isto antes de alterar código. |
docs/Tool-Router.md | O que você quer fazer → a ferramenta que faz isso. Escrito à mão, nas palavras que as pessoas usam. Comece aqui se você conhece a tarefa, mas não a ferramenta. |
docs/Tool-Reference.md | Todas as 128 ferramentas com seus argumentos, agrupadas por família. Gerado a partir das definições de ferramentas, então não pode divergir do código. |
docs/MCP-Tools.md | Como o servidor se comporta. Os perfis, fluxos de trabalho de caminho dourado, semântica de URI e lock do ADT, o modelo de resposta/erro e uma matriz de solução de problemas. Escrito para humanos e para agentes que dirigem o servidor. |
docs/ABAP-Skills.md | As 20 habilidades SAP/ABAP e como elas se mapeiam nessas ferramentas. |
docs/Development-Skills.md | As 35 habilidades gerais de engenharia incluídas. |
docs/JSON-RPC.md | Notas de design e protocolo para as ferramentas JSON-RPC / RFC, lidas do código-fonte ABAP de /IWBEP/CL_JSRPC_*: o protocolo de fio, a garantia de LUW por trás de lotes e as armadilhas. |
docs/Authentication.md | Os dois modos de logon sem senha: SSO Kerberos e certificados X.509 para usuários de serviço e técnicos. O que o SAP precisa configurado e por que isso é TLS mútuo em vez de SNC. |
Recursos
- Dois modos de logon sem senha — SPNEGO/Kerberos com o ticket do usuário do Windows conectado, ou um certificado de cliente X.509 para um usuário de serviço ou técnico que não tem identidade Kerberos. Nenhuma senha SAP é armazenada ou enviada de qualquer forma, e ambos se auto-recuperam quando a sessão expira.
- Transporte HTTP streamable, para uma equipe compartilhando um contêiner — defina
ABAP_MCP_TRANSPORT=httpe cada cliente MCP conectado autentica com seu próprio token portador OAuth 2.0 do SAP, então cada sessão obtém seu próprio logon SAP em vez de todos compartilharem um usuário técnico. stdio permanece o padrão para um único cliente iniciado localmente. Veja §12 e Executando sobre HTTP abaixo. - Leia qualquer objeto pelo nome —
readAbapObjectresolve um nome para seu código-fonte em uma chamada; nenhuma URL ADT para descobrir ou criar manualmente. - Descreva uma tabela —
describeAbapTableretorna campos, tipos DDIC, flags de chave e tabelas de verificação. - Gerenciamento de objetos — pesquise, leia, crie, modifique, exclua e ative objetos ABAP.
- Levante uma convenção de nomenclatura —
searchPackagesencontra pacotes por padrão (["ZPP_*","Z_PP*"]) e expande cada um em seus objetos agrupados por tipo, em uma chamada. - Fluxo de trabalho de código-fonte — lock → editar → verificação de sintaxe → ativar → unlock, com tratamento de transport.
- Inteligência de código — conclusão, definições, referências de uso, ABAP Doc, pretty printer, ATC, ABAP Unit, refatoração (renomear, extrair método).
- Chame módulos de função RFC —
callFunctionViaJsonRpcexecuta um módulo de função habilitado para RFC e valida a solicitação contra sua assinatura real, lida do sistema. - Lote de chamadas RFC em uma única LUW —
callFunctionsViaJsonRpcenvia vários módulos de função em uma única solicitação, o que permite que um BAPI de atualização e seuBAPI_TRANSACTION_COMMITcompartilhem uma LUW. - Acesso a dados —
tableContentse SELECTsrunQueryad-hoc. - Veja o sistema em execução —
listLoggedOnUsersresponde "quem está conectado" deTH_USER_LIST, os dados por trás deSM04;readProfileParameterslê valores RZ11 em uma única ida e volta; echeckLogonConfigurationdiz qual autenticação o sistema realmente aceita. - Habilidades de agente incluídas — 54 habilidades sob
skills/, para ABAP (Clean ABAP, RAP, CDS, ATC, abapGit…) e engenharia geral (TDD, revisão de código, diagnóstico de bugs), servidas como recursos e viareadSkill. - Autodocumentável — os guias abaixo são servidos pelo próprio servidor, como recursos MCP (
abap-adt://guides/…) e por meio da ferramentareadServerGuide, para que um agente possa consultar um fluxo de trabalho ou um argumento no meio da tarefa sem sair da sessão.
Pré-requisitos
- Um sistema SAP ABAP acessível via ADT.
/sap/bc/adtdeve estar ativo emSICF. Para as ferramentas RFC,/sap/gw/jsonrpctambém deve estar ativo (SAP_GWFND), e seu usuário precisa deS_RFCpara os grupos de funções que você chama. - Uma forma de fazer logon sem senha, uma de:
- Um logon Kerberos funcional — o sistema SAP deve aceitar SPNEGO e você deve ter um ticket válido (
klist). Este é o padrão e precisa de Windows como enviado: o bootstrap chamaC:\Windows\System32\curl.exe --negotiate, que precisa do backend Schannel/SSPI do curl. Em outras plataformas, aponteSSO_CURL_PATHpara um curl compilado com suporte a GSS-API.- Um certificado de cliente X.509 — para um usuário de serviço ou técnico que não tem identidade Kerberos. Não precisa de curl nem Windows. O SAP precisa ser configurado para isso: a porta ICM deve solicitar um certificado (
VCLIENTemicm/server_port_<n>, que substituiicm/HTTPS/verify_client), a CA emissora confiável emSTRUSTe um mapeamentoCERTRULEpara o usuário. Configuração completa — incluindo como verificar isso a partir deste servidor — emdocs/Authentication.md. - Um cliente OAuth 2.0 — para um ambiente ABAP SAP BTP, onde não há realm Kerberos nem ICM para configurar, ou para um sistema on-premise que publica ADT por meio de
SOAUTH2. Uma chave de serviço BTP já é uma dessas. Veja §11.
- Um certificado de cliente X.509 — para um usuário de serviço ou técnico que não tem identidade Kerberos. Não precisa de curl nem Windows. O SAP precisa ser configurado para isso: a porta ICM deve solicitar um certificado (
- Um logon Kerberos funcional — o sistema SAP deve aceitar SPNEGO e você deve ter um ticket válido (
- Node.js (LTS) e npm — verifique com
node -venpm -v.
Instalação
git clone --recurse-submodules https://github.com/Ciltress/sap-abap-mcp.git
cd sap-abap-mcp
npm install
npm run build
--recurse-submodulesimporta: as habilidades gerais de engenharia vivem em um submódulo, e sem ele esse diretório fica vazio e o servidor oferece 35 habilidades a menos. Já clonou? Executegit submodule update --init --recursive.
O pacote
npx mcp-abap-abap-adt-apino npm é o servidor upstream e não inclui o bootstrap SSO nem as ferramentas RFC. Compile este repositório a partir do código-fonte.
Configurar
Copie .env.example para .env e preencha com seu sistema:
SAP_URL=https://your-sap-server.example.com:44301
SAP_USER=YOUR_SAP_USER
SAP_CLIENT=100
SAP_LANGUAGE=EN
SAP_URL e SAP_USER são obrigatórios; SAP_CLIENT e SAP_LANGUAGE são opcionais, mas recomendados. Três dos quatro modos de logon não precisam de nenhum SAP_PASSWORD.
Nunca faça commit de .env; ele já está em .gitignore.
| Variável opcional | Efeito |
|---|---|
SAP_SYSTEM_ID | O ID do sistema, como em sy-sysid (ex.: DEV). Anunciado no momento da conexão para que um cliente possa escolher entre vários servidores pelo nome. Veja Mais de um sistema. |
NODE_TLS_REJECT_UNAUTHORIZED=0 | Aceitar um certificado de uma CA interna/desconhecida. Somente desenvolvimento. |
SSO_CURL_PATH | Caminho para um binário curl com suporte a SPNEGO, se não for o do sistema Windows. |
SAP_JSONRPC_PATH | Substituir o caminho ICF JSON-RPC quando o nó é publicado sob um alias. |
ABAP_MCP_PROFILE | Quais ferramentas este servidor lista — e, portanto, quais ele responderá. Veja abaixo. |
ABAP_MCP_MAX_RESPONSE_BYTES | Teto para uma única resposta, em bytes. 0 o remove. |
ABAP_MCP_GATE | off pula a verificação de capacidade na inicialização que retém ferramentas que esta versão não pode servir. |
ABAP_MCP_RFC_FALLBACK | Iniciar mesmo quando o SAP recusa o nó ADT para este usuário, mantendo as ferramentas RFC. Veja docs/Authentication.md. |
SAP_FALLBACK_BOOTSTRAP_PATH | Qual nó ICF esse fallback faz logon. Só precisa emitir um cookie de sessão e um token CSRF. |
ABAP_MCP_TRANSPORT | stdio (padrão) ou http. Veja Executando sobre HTTP. |
ABAP_MCP_HTTP_PORT | Porta para o endpoint HTTP Streamable. Padrão 3000. Só lido no modo http. |
ABAP_MCP_HTTP_HOST | Endereço para vincular no modo http. Padrão 0.0.0.0. |
ABAP_MCP_HTTP_ALLOWED_ORIGINS | Origens de navegador permitidas para chamar /mcp, separadas por vírgula; * permite qualquer uma. Vazio (o padrão) não atende nenhuma. Clientes que não enviam Origin não são afetados. |
ABAP_MCP_HTTP_ALLOWED_HOSTS | Valores Host aos quais este servidor responde no modo http, separados por vírgula. Vazio aceita qualquer um. |
ABAP_MCP_HTTP_RATE_LIMIT | Solicitações por minuto por sessão MCP no modo http. 0 (o padrão) está desligado. |
Dimensionando o servidor para seu cliente
As três variáveis ABAP_MCP_* existem por um motivo: um cliente que não pode buscar esquemas de ferramentas sob demanda paga pela lista inteira de ferramentas a cada turno. Claude Code adia esquemas e deve permanecer no padrão; um modelo de 8B com janela de 128k gasta um sexto de seu contexto antes de a conversa começar, e é daí que vem "o mesmo prompt funciona metade das vezes".
ABAP_MCP_PROFILE — não definido significa all, então uma configuração existente permanece inalterada.
| perfil | tools/list | custo por turno | para |
|---|---|---|---|
core | 9 | ~2.737 tokens | ler um sistema e concluir uma edição |
analyst | 18 | ~3.982 tokens | somente leitura: dicionário, dados de tabela, chamadas RFC |
rfc | 10 | ~2.900 tokens | um usuário com direitos RFC, mas sem S_DEVELOP, onde as ferramentas ADT não podem funcionar |
dev | 49 | ~8.034 tokens | o ciclo de edição mais testes, ATC, transports, refatoração |
all | 129 | ~17.759 tokens | o padrão; certo para clientes que buscam esquemas sob demanda |
As contagens incluem healthcheck, que fica fora de todos os perfis porque é a ferramenta que responde "qual perfil estou executando?".
Um perfil não é um filtro de conveniência. Uma ferramenta fora do perfil ativo não é listada e não é roteada, então não pode ser chamada — o que faz de analyst uma garantia de que nada edita código-fonte, em vez de um menu menor. Chamadas fora do perfil recebem um erro que diz isso, em vez de "ferramenta desconhecida", então não há motivo para tentar novamente. Um nome de perfil não reconhecido para o servidor na inicialização, em vez de cair para all — servir silenciosamente 129 ferramentas para algo que pediu 9 é exatamente a falha que os perfis existem para prevenir.
core é pequeno porque uma ferramenta, editAbapSource, é o ciclo de escrita: lock, escrever, ativar, unlock, liberando o lock mesmo quando uma etapa falha. As quatro etapas separadas permanecem em dev e all.
ABAP_MCP_MAX_RESPONSE_BYTES — a lista de ferramentas é um custo fixo que um perfil pode reduzir; uma resposta é ilimitada. No core, a lista completa de ferramentas tem cerca de 11KB, enquanto uma única adtDiscovery tem cerca de 42KB. Não definido segue o perfil (core 24.000 bytes, analyst 32.000, dev 48.000, all sem teto); 0 a remove.
Uma resposta acima do orçamento é retida e substituída por JSON válido — status:"truncated", o bytes original, o budget, um preview de 2.000 bytes e um nextStep — nunca por um fragmento cortado, que não seria analisado e apenas convidaria à mesma tentativa repetida.
ABAP_MCP_GATE — antes de listar qualquer coisa, o servidor pergunta ao sistema o que ele suporta e retém as ferramentas cujas coleções ADT estão ausentes. No DEV, essas são as 10 ferramentas abapGit e as 3 ferramentas de service-binding, que de outra forma responderiam HTTP 400. Isso custa uma ida e volta de descoberta por processo, só pode encurtar a lista, e qualquer falha deixa todas as ferramentas listadas. ABAP_MCP_GATE=off pula isso.
healthcheck relata todos os três: o perfil ativo, responseBudgetBytes e qualquer coisa retida.
Um certificado em vez de Kerberos
Para um usuário de serviço ou técnico, adicione um certificado — isso sozinho alterna o modo:
SAP_USER=CLAUDEAGENT # the user CERTRULE maps the certificate to
SAP_CERT_FILE=C:\Users\svc_agent\SNC\sec\claudeagent.p12
SAP_CERT_PASSPHRASE=<PKCS#12 password / PSE PIN>
| Variável opcional | Efeito |
|---|---|
SAP_AUTH_MODE | kerberos, certificate, oauth ou password. Necessário apenas para forçar um modo enquanto outro está configurado. |
SAP_CERT_KEY_FILE | A chave privada, quando não está em SAP_CERT_FILE. |
SAP_CA_FILE | Pacote de CAs para verificar o certificado da própria SAP, em vez de desabilitar a verificação TLS. |
Isso é TLS mútuo, não SNC — ADT é HTTPS. Um certificado que já funciona para RFC/SNC pode ser reutilizado e seu mapeamento
CERTRULEé mantido, mas a ACLSNC0não tem papel e o ICM precisa deicm/HTTPS/verify_client.docs/Authentication.mdcobre as diferenças, a armadilha dosapgenpse export_p12/ OpenSSL 3 e como ler um certificado rejeitado.
Um cliente OAuth 2.0, para BTP e para SOAUTH2
Para um ambiente ABAP do SAP BTP, ou um sistema on-premise que publica ADT atrás de um servidor de autorização. Definir o ID do cliente alterna o modo:
SAP_OAUTH_TOKEN_URL=https://your-tenant.authentication.eu10.hana.ondemand.com/oauth/token
SAP_OAUTH_CLIENT_ID=sb-abap-agent!t1234 # 'clientid' in a BTP service key
SAP_OAUTH_CLIENT_SECRET=<'clientsecret'>
On-premise, o endpoint está no próprio host SAP — https://<host>:<port>/sap/bc/sec/oauth2/token — e o cliente é o registrado em SOAUTH2.
| Variável opcional | Efeito |
|---|---|
SAP_OAUTH_GRANT | client_credentials (padrão), refresh_token, password ou static. |
SAP_OAUTH_SCOPE | Escopos a solicitar. Não definido pede os padrões do cliente, o que geralmente é o certo. |
SAP_OAUTH_REFRESH_TOKEN | Para a concessão refresh_token; seleciona-a por si só. |
SAP_OAUTH_TOKEN | Um token emitido em outro lugar, usado como está. Nada pode renová-lo. |
SAP_OAUTH_CLIENT_AUTH | basic (padrão) ou post, quando um servidor rejeita um segredo que está correto. |
O token faz logon uma vez. Depois disso, o cookie de sessão SAP carrega cada solicitação, como nos outros modos — então um token que expira em cinco minutos não é um problema. Observe que um cliente OAuth 2.0 no AS ABAP é um usuário em
SU01: umSAP_OAUTH_CLIENT_SECRETerrado conta contralogin/fails_to_user_lockcomo uma senha, então uma solicitação de token recusada nunca é repetida. O fluxo de authorization-code não é implementado — ele precisa de um navegador, que um servidor iniciado via stdio não tem como abrir; complete-o uma vez manualmente e passe o refresh token.docs/Authentication.md§11 tem o detalhe, incluindo quais falhas são travadas e por quê.
Uma senha, quando não há mais nada
Para um sistema sem Kerberos nem certificados — um sandbox, um trial, qualquer coisa fora do domínio:
SAP_USER=CLAUDEAGENT
SAP_PASSWORD=<the password>
O último recurso, e não intercambiável com os outros dois. Um ticket ausente ou um certificado não mapeado é simplesmente recusado; uma senha errada conta contra
login/fails_to_user_locke bloqueia esse usuário para todos os consumidores dele, não apenas este servidor. A implementação se recusa a repetir uma senha rejeitada por esse motivo — um logon falho, travado, não importa quantas ferramentas sejam chamadas. Prefira um certificado para qualquer coisa não supervisionada.docs/Authentication.md§10 tem o detalhe.
Aponte o cliente para o entry point construído com caminhos absolutos:
{
"mcpServers": {
"sap-abap-dev-100": {
"command": "node",
"args": ["C:/path/to/sap-abap-mcp/dist/index.js"],
"env": {
"SAP_URL": "https://your-sap-server.example.com:44301",
"SAP_USER": "YOUR_SAP_USER",
"SAP_SYSTEM_ID": "DEV",
"SAP_CLIENT": "100",
"SAP_LANGUAGE": "EN"
}
}
}
}
O bloco env do cliente vence sobre .env. Execute npm run start para iniciar o servidor manualmente, ou npm run dev para dirigi-lo através do MCP Inspector.
Para um cliente que carrega todo o esquema de ferramentas a cada turno, adicione um perfil ao mesmo bloco:
"env": { "…": "…", "ABAP_MCP_PROFILE": "core" }
Em Docker
Por padrão, o servidor fala MCP sobre stdio, então não há porta para publicar — o cliente inicia o contêiner e conversa com ele via stdin/stdout. Esta seção cobre esse padrão; para um contêiner ao qual vários membros da equipe se conectam pela rede, veja Executando sobre HTTP abaixo.
docker build -t abap-adt-mcp .
{
"mcpServers": {
"sap-abap-dev-100": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"--env-file",
"C:/path/to/.env",
"abap-adt-mcp"
]
}
}
}
Todos os quatro modos de logon funcionam aqui, mas uma credencial é algo que o contêiner precisa receber, e --env-file não é dotenv — o Docker não remove aspas, não lê export e não descarta # comment finais, então um valor que o dotenv teria limpo chega verbatim. Caminhos do Windows em .env precisam ser substituídos pelos montados. Kerberos é o modo que mais exige do contêiner e OAuth o que menos: um token é buscado pela rede, então nada precisa ser montado.
Modo certificado — monte o material da chave somente leitura, e a CA que assina o certificado da SAP junto:
docker run -i --rm --env-file .env \
-v /host/certs:/certs:ro \
-e SAP_CERT_FILE=/certs/agent.p12 \
-e SAP_CA_FILE=/certs/corporate-root.pem \
abap-adt-mcp
Modo Kerberos — a imagem carrega um curl construído contra GSS-API e kinit, então o que resta é um realm e uma credencial:
docker run -i --rm --env-file .env \
-v /etc/krb5.conf:/etc/krb5.conf:ro \
-v /host/agent.keytab:/krb5/agent.keytab:ro \
-e SAP_KRB_KEYTAB=/krb5/agent.keytab \
-e SAP_KRB_PRINCIPAL=SVC_AGENT@CORP.EXAMPLE.COM \
abap-adt-mcp
Modo OAuth 2.0 — nada para montar; a credencial é buscada:
docker run -i --rm --env-file .env \
-e SAP_OAUTH_TOKEN_URL=https://your-tenant.authentication.eu10.hana.ondemand.com/oauth/token \
-e SAP_OAUTH_CLIENT_ID='sb-abap-agent!t1234' \
-e SAP_OAUTH_CLIENT_SECRET=<clientsecret> \
abap-adt-mcp
Modo senha — Último recurso!
docker run -i --rm --env-file .env \
-e SAP_USER=YourUser \
-e SAP_PASSWORD=YourPassword \
abap-adt-mcp
Três coisas para saber antes de recorrer a ele:
- Um keytab, não seu próprio ticket. Um contêiner não pode emprestar a credencial da sessão como um logon interativo faz. Em um host Linux, você pode montar o cache de tickets que já tem (
-v /tmp/krb5cc_1000:/krb5/ccache:ro -e KRB5CCNAME=FILE:/krb5/ccache), mas ele expira com o do host. Em um host Windows, nenhum dos dois funciona: o TGT vive no cache LSA e não pode ser gravado em um arquivo — use o modo certificado, que não precisa de nada disso. O keytab é a única credencial que roda sem supervisão e a única que sobrevive à vida útil do ticket. - Verifique o certificado da SAP, ou saiba que não está verificando. A imagem confia apenas no pacote de CAs público, então uma CA interna que o host confia é desconhecida aqui e o handshake falha com
UNABLE_TO_GET_ISSUER_CERT_LOCALLY. Monte a CA raiz e aponteSAP_CA_FILEpara ela. Carregar um.envde desktop em massa esconde isso:NODE_TLS_REJECT_UNAUTHORIZED=0é uma configuração de desenvolvimento e não tem lugar em uma imagem implantada. - Construa a partir de um clone
--recurse-submodules.skills/Developmenté um submódulo; sem ele, a imagem entrega 35 habilidades a menos.
A imagem é Debian em vez de Alpine por um motivo: o curl do Alpine é construído sem GSS-API, e tal curl não falha — ele simplesmente nunca envia um token, e a SAP responde com o mesmo 401 que um ticket expirado produz. O que está faltando é nomeado na inicialização, em stderr, antes que uma sessão seja tentada. Para perguntar ao contêiner com qual credencial ele terminou, dê a ele um comando em vez do servidor:
docker run --rm --env-file .env -v /host/agent.keytab:/krb5/agent.keytab:ro \
-e SAP_KRB_KEYTAB=/krb5/agent.keytab abap-adt-mcp klist
docs/, skills/ e AGENTS.md são copiados para a imagem de propósito — o servidor os lê em tempo de execução para servir os recursos readServerGuide, readSkill e abap-adt://. docs/Authentication.md §7 tem a configuração completa do contêiner, modo por modo.
Executando sobre HTTP
Tudo acima inicia um servidor para um cliente via stdio. Defina ABAP_MCP_TRANSPORT=http para executar um contêiner de longa duração ao qual toda uma equipe se conecta pela rede, usando o transporte Streamable HTTP do SDK em /mcp.
O modo HTTP muda quem faz logon, não o que uma sessão pode fazer depois de logada: nenhum dos quatro modos acima — SAP_AUTH_MODE, SAP_CERT_FILE, SAP_OAUTH_CLIENT_ID, SAP_PASSWORD — se aplica aqui, porque não há um único logon compartilhado para configurar. Em vez disso, cada cliente MCP autentica com seu próprio token bearer OAuth 2.0 da SAP: ele envia Authorization: Bearer <token> em initialize, e esse token se torna a identidade SAP da própria sessão — seu próprio logon ADT, seus próprios locks, sua própria trilha de auditoria. Dois membros da equipe conectando ao mesmo tempo obtêm duas sessões independentes, nunca um usuário técnico compartilhado.
docker build -t abap-adt-mcp .
docker run --rm -p 3000:3000 \
-e SAP_URL=https://your-sap-server.example.com:44301 \
-e SAP_USER=CLAUDEAGENT \
-e SAP_CLIENT=100 \
-e ABAP_MCP_TRANSPORT=http \
abap-adt-mcp
SAP_USER ainda é necessário (todo modo não-senha precisa de um nome de usuário placeholder para construir o cliente ADT), mas nunca é transmitido e não tem influência sobre quem uma sessão realmente é — a SAP decide isso a partir do token de cada sessão, como em §11.
| Variável | Efeito |
|---|---|
ABAP_MCP_TRANSPORT=http | Alterna de stdio para Streamable HTTP. |
ABAP_MCP_HTTP_PORT | Porta para escutar. Padrão 3000. |
ABAP_MCP_HTTP_HOST | Endereço para vincular. Padrão 0.0.0.0. |
ABAP_MCP_HTTP_ALLOWED_ORIGINS | Origens de navegador permitidas a chamar /mcp, separadas por vírgula; * permite qualquer. Vazio (padrão) não atende nenhuma. |
ABAP_MCP_HTTP_ALLOWED_HOSTS | Valores Host respondidos, separados por vírgula. Vazio aceita qualquer. |
ABAP_MCP_HTTP_RATE_LIMIT | Solicitações por minuto por sessão. 0 (padrão) está desligado. |
Registre um cliente da mesma forma que qualquer servidor MCP Streamable HTTP remoto, apontando para /mcp e fornecendo o token OAuth SAP do próprio usuário:
{
"mcpServers": {
"sap-abap-dev-100": {
"url": "http://your-host:3000/mcp",
"headers": { "Authorization": "Bearer <your SAP OAuth access token>" }
}
}
}
Uma solicitação sem credencial é rejeitada com 401 antes de qualquer tráfego SAP; um token que a própria SAP recusa também é 401, com a mensagem da própria SAP. Não há segredo compartilhado separado para configurar para o transporte em si — o token SAP é o controle de acesso.
Um cliente que prefere não ser desconectado cada vez que seu token de acesso expira pode enviar seu próprio refresh token como X-SAP-Refresh-Token em vez disso, e a sessão se renova na concessão refresh_token. Isso precisa de SAP_OAUTH_TOKEN_URL e SAP_OAUTH_CLIENT_ID no contêiner — o registro do cliente contra o qual um refresh token é resgatado é o da implantação, enquanto o refresh token, e portanto a identidade SAP, permanece do chamador.
Origens de navegador são recusadas a menos que ABAP_MCP_HTTP_ALLOWED_ORIGINS as nomeie: esse é o guarda de DNS-rebinding que a especificação MCP pede que o servidor aplique a si mesmo, já que uma solicitação de rebinding nunca passa pelo proxy na frente. Origens na allow-list recebem os cabeçalhos CORS correspondentes. Clientes que não enviam Origin — todo cliente MCP que não é uma página web — não são afetados.
O docker-entrypoint.sh do contêiner pula sua configuração Kerberos automaticamente quando ABAP_MCP_TRANSPORT=http está definido, já que a escolha de resolveAuthMode() não descreve nada real quando cada sessão faz logon com seu próprio token. A terminação TLS é deixada para o que estiver na frente do contêiner (um reverse proxy ou ingress), o mesmo que para qualquer outro serviço HTTP interno. Detalhes completos, incluindo a mecânica da concessão OAuth à qual isso se reduz, estão em docs/Authentication.md §12.
Mais de um sistema
Um servidor está vinculado a um sistema e um cliente por toda a sua vida — nenhum pode ser alternado em tempo de execução. Então registre uma entrada por sistema/cliente e dê a cada um seu SAP_SYSTEM_ID:
"sap-abap-dev-100": { "env": { "SAP_SYSTEM_ID": "DEV", "SAP_CLIENT": "100", "…": "…" } },
"sap-abap-dev-200": { "env": { "SAP_SYSTEM_ID": "DEV", "SAP_CLIENT": "200", "…": "…" } },
"sap-abap-q01-100": { "env": { "SAP_SYSTEM_ID": "Q01", "SAP_CLIENT": "100", "…": "…" } }
Cada servidor então se anuncia no instructions que retorna no momento da conexão:
Este servidor está vinculado ao sistema SAP DEV, cliente 100 (https://…:44301). Ele não pode alternar sistema ou cliente em tempo de execução — ambos são fixados pelo ambiente com o qual foi iniciado. Se uma solicitação nomear um sistema ou cliente diferente, use o servidor MCP configurado para aquele; se nenhum estiver registrado, diga isso em vez de agir aqui. É isso que permite a um agente rotear "verifique isso no DEV client 200" para o servidor certo sem chamar nada.
healthcheckrelata a mesma identidade para um servidor que precisa ser consultado diretamente.
A declaração é verificada. O SAP nomeia o sistema e o client no cookie de sessão que define no logon (SAP_SESSIONID_DEV_100), então o servidor sabe a que está realmente conectado. Se isso discordar de SAP_SYSTEM_ID — uma entrada copiada e colada apontando para o host errado, por exemplo — healthcheck carrega um WARNING e é registrado em log de forma bem visível na inicialização. Vale a pena ficar atento, porque todas as ferramentas continuam funcionando perfeitamente; só que no sistema errado.
Tour rápido
Leia qualquer objeto pelo nome — sem necessidade de descoberta de URL:
{"tool":"readAbapObject","args":{"objectName":"ZCL_MY_CLASS"}}
{"tool":"describeAbapTable","args":{"tableName":"T000"}}
Levante toda uma convenção de nomenclatura — os padrões são normalizados para você, então zpp_lab vira ZPP_LAB*:
{ "tool": "searchPackages", "args": { "patterns": ["ZPP_*", "Z_PP*"] } }
// -> each package with its objects grouped by type, sub-packages, and a \`truncated\` flag
Chame um módulo de função — a assinatura é lida primeiro e a solicitação é validada contra ela:
{"tool":"readAbapFunctionModule","args":{"functionModuleName":"STFC_CONNECTION"}}
{"tool":"callFunctionViaJsonRpc","args":{"functionModuleName":"STFC_CONNECTION",
"inputParameters":{"REQUTEXT":"hello"}}}
Um BAPI e seu commit devem viajar em um único lote, ou o commit cai em sua própria LUW e as alterações do BAPI são perdidas:
{
"tool": "callFunctionsViaJsonRpc",
"args": {
"calls": [
{
"functionModuleName": "BAPI_USER_LOCK",
"inputParameters": { "USERNAME": "DEVUSER" },
},
{
"functionModuleName": "BAPI_TRANSACTION_COMMIT",
"inputParameters": { "WAIT": "X" },
},
],
},
}
O ciclo completo de escrita (bloquear → modificar → verificar → ativar → desbloquear), o depurador, o ATC e todos os outros fluxos de trabalho estão em docs/MCP-Tools.md §4.
Trabalhando com objetos ABAP
Três ferramentas cobrem a maior parte do que você precisa, e cada uma recebe um nome em vez de uma URL ADT:
| O que você quer | Ferramenta |
|---|---|
| O código-fonte de uma classe, programa, include, grupo de funções ou módulo | readAbapObject |
| Como uma tabela, estrutura ou visão se parece | describeAbapTable |
| Tudo por trás de uma convenção de nomenclatura | searchPackages |
{"tool":"readAbapObject", "args":{"objectName":"ZCL_MY_CLASS"}}
{"tool":"describeAbapTable","args":{"tableName":"T000"}}
{"tool":"searchPackages", "args":{"patterns":["ZPP_*","Z_PP*"]}}
readAbapObject retorna os metadados e o código-fonte em uma única chamada. Quando um nome pertence a vários objetos — ZPP_EXT_LABEL_DATA é tanto um grupo de funções quanto um módulo de função — ele escolhe o mais específico e informa você, via ambiguous:true e alternatives; passe objectType para forçar a escolha. Objetos sem código-fonte retornam com hasSource:false e um ponteiro para a ferramenta certa.
describeAbapTable fornece nomes de campos, tipos DDIC, comprimentos, flags de chave, elementos de dados, domínios e tabelas de verificação — a tabela de verificação é o alvo da chave estrangeira, que é a maneira mais rápida de ver como duas tabelas se juntam. objectStructure não retorna campos para uma tabela, e tableContents retorna linhas em vez de uma definição, então nenhuma responde "como essa tabela se parece".
Regras que valem a pena colocar no prompt do sistema do seu cliente
- Prefira as ferramentas por nome. Só recorra a
searchObject→objectStructure→getObjectSourcequando precisar dos resultados intermediários. Nunca monte manualmente um caminho/sap/bc/adt/.... - Selecione com eficiência. As tabelas SAP são grandes. Sempre restrinja
SELECTs com uma cláusulaWHERE, e useSELECT SINGLE(todos os campos-chave conhecidos) ouUP TO n ROWScaso contrário.
SELECT vgbel FROM vbrp WHERE vbeln = @lv_vbeln INTO @DATA(lv_vgbel) UP TO 1 ROWS.
EXIT.
ENDSELECT.
O SAP é desacoplado do seu sistema de arquivos: ler o código-fonte o retorna apenas como resultado da ferramenta, e escrever um arquivo local não muda nada no SAP. Cópias locais são úteis apenas para comparação, nada mais.
READMEs anteriores listavam
GetTable,GetStructureeGetTypeInfo. Eles pertencem ao projeto separadomcp-abap-adt, não a este servidor.
Desenvolvimento
npm run build # tsc -> dist/
npm test # vitest: parser, tool catalogue, JSON-RPC handler (no SAP system needed)
npx tsc --noEmit # type check only
Os testes ficam em src/__tests__ e rodam totalmente offline — a suíte JSON-RPC exercita o handler de ponta a ponta contra um nó SAP Gateway falso.
Contra um sistema real (precisa de um ticket Kerberos), a verificação de ponta a ponta é:
npm run build
node scripts/live-jsonrpc-check.mjs # add NODE_TLS_REJECT_UNAUTHORIZED=0 for an internal CA
Ela dirige o servidor compilado via MCP stdio exatamente como um cliente faria, e só chama módulos de função somente leitura.
Adicionar uma ferramenta é uma mudança de um arquivo — veja docs/MCP-Tools.md §10.
Solução de problemas
| Sintoma | Causa / correção |
|---|---|
HTTP 401 em toda chamada | Modo Kerberos: sem ticket válido, ou o sistema não aceita SPNEGO — verifique klist e sua conexão VPN/domínio. Modo certificado: veja docs/Authentication.md §6. |
curl nicht gefunden | O bootstrap de SSO não conseguiu encontrar o curl — defina SSO_CURL_PATH. |
SAP rejected the client certificate | O mapeamento CERTRULE, icm/HTTPS/verify_client, ou a confiança da CA em STRUST. O erro nomeia o assunto que foi apresentado — compare-o com CERTRULE. |
unable to get local issuer certificate | CA interna. Defina NODE_TLS_REJECT_UNAUTHORIZED=0 (apenas desenvolvimento). |
Ferramentas RFC retornam reachable:false | Execute checkJsonRpcEndpoint. Ele separa um nó SICF /sap/gw/jsonrpc inativo de um problema de CSRF ou autorização. |
-32601 de um módulo de função | Ele não existe, não está habilitado para RFC, ou S_RFC nega seu grupo de funções. |
| O cliente não mostra ferramentas | Verifique o caminho absoluto para dist/index.js e que npm run build foi executado. |
Mais em docs/MCP-Tools.md §8.
Contribuindo
- Faça um fork do repositório
git checkout -b feature/your-feature-name- Faça sua alteração, mantenha
npm testenpx tsc --noEmitverdes git commit -m "Add some feature"egit push origin feature/your-feature-name- Abra um pull request