guniweb-sap-mcp 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/index.js +17 -13
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -92,6 +92,8 @@ The demo starts an in-memory OData V2 gateway on localhost (three services with
|
|
|
92
92
|
npm install -g guniweb-sap-mcp
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
Or [run it as a container](#run-as-a-container): `docker run --rm -p 8808:8808 -e SAP_MCP_DEMO=true ghcr.io/guniweb/guniweb-sap-mcp:latest`
|
|
96
|
+
|
|
95
97
|
### 2. Configure
|
|
96
98
|
|
|
97
99
|
```bash
|
|
@@ -121,6 +123,27 @@ guniweb-sap-mcp --transport http --port 8808 --allow-write # writes are OFF by
|
|
|
121
123
|
guniweb-sap-mcp --transport http --port 8808 --api-key <mcp-key> --webhook-secret <idoc-secret>
|
|
122
124
|
```
|
|
123
125
|
|
|
126
|
+
### Run as a container
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Look around first — mock S/4HANA inside the container, no SAP system needed
|
|
130
|
+
docker run --rm -p 8808:8808 -e SAP_MCP_DEMO=true ghcr.io/guniweb/guniweb-sap-mcp:latest
|
|
131
|
+
|
|
132
|
+
# Against a real system (pin the version in production)
|
|
133
|
+
docker run -d --name sap-mcp -p 8808:8808 --env-file .env \
|
|
134
|
+
ghcr.io/guniweb/guniweb-sap-mcp:<version>
|
|
135
|
+
|
|
136
|
+
# Flags go straight to the server — writes stay off unless you say so
|
|
137
|
+
docker run -d -p 8808:8808 --env-file .env \
|
|
138
|
+
ghcr.io/guniweb/guniweb-sap-mcp:<version> --allow-write
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The image is built **from the published npm package**, not from a second build path — what runs in the container is the artifact you would have installed with `npm install`, so the two cannot drift apart. HTTP transport on port 8808 is the default inside the image (`SAP_MCP_TRANSPORT` / `SAP_MCP_PORT`); it runs as a non-root user, answers a health check on `/healthz`, and is published for `linux/amd64` and `linux/arm64` with build provenance.
|
|
142
|
+
|
|
143
|
+
Not included: **RFC/BAPI**. That path needs the SAP NW RFC SDK, which SAP licenses to customers only and which therefore cannot ship in a public image. Everything else — OData V2/V4, IDoc over HTTP/XML — works. For RFC, install the SDK on the host and run the server from npm ([Setup for SAP ECC](#setup-for-sap-ecc)).
|
|
144
|
+
|
|
145
|
+
A `docker-compose.yml` that puts n8n next to the server is in the repository.
|
|
146
|
+
|
|
124
147
|
#### Production hardening
|
|
125
148
|
|
|
126
149
|
| Variable / Flag | Default | Description |
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {readFileSync,existsSync,watchFile,watch,unwatchFile,mkdirSync,writeFileSync,renameSync,unlinkSync}from'fs';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import ui from'pino';import {minimatch}from'minimatch';import {z as z$1}from'zod/v4';import {lookup}from'dns/promises';import {connect}from'tls';import Vt,{isAxiosError}from'axios';import {AsyncLocalStorage}from'async_hooks';import {randomUUID,randomBytes,timingSafeEqual,createHash}from'crypto';import {jwtVerify,decodeJwt,createRemoteJWKSet}from'jose';import {SignedXml}from'xml-crypto';import {Agent}from'https';import {createServer}from'http';import {resolve,join,dirname}from'path';import {z as z$2}from'zod';import {readFile}from'fs/promises';import {XMLParser,XMLBuilder}from'fast-xml-parser';import {McpServer,ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {createMcpExpressApp}from'@modelcontextprotocol/sdk/server/express.js';import {SSEServerTransport}from'@modelcontextprotocol/sdk/server/sse.js';import {StreamableHTTPServerTransport}from'@modelcontextprotocol/sdk/server/streamableHttp.js';var
|
|
2
|
+
import {readFileSync,existsSync,watchFile,watch,unwatchFile,mkdirSync,writeFileSync,renameSync,unlinkSync}from'fs';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import ui from'pino';import {minimatch}from'minimatch';import {z as z$1}from'zod/v4';import {lookup}from'dns/promises';import {connect}from'tls';import Vt,{isAxiosError}from'axios';import {AsyncLocalStorage}from'async_hooks';import {randomUUID,randomBytes,timingSafeEqual,createHash}from'crypto';import {jwtVerify,decodeJwt,createRemoteJWKSet}from'jose';import {SignedXml}from'xml-crypto';import {Agent}from'https';import {createServer}from'http';import {resolve,join,dirname}from'path';import {z as z$2}from'zod';import {readFile}from'fs/promises';import {XMLParser,XMLBuilder}from'fast-xml-parser';import {McpServer,ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {createMcpExpressApp}from'@modelcontextprotocol/sdk/server/express.js';import {SSEServerTransport}from'@modelcontextprotocol/sdk/server/sse.js';import {StreamableHTTPServerTransport}from'@modelcontextprotocol/sdk/server/streamableHttp.js';var vn=16;function ke(t,e,n,r=process.env){if(t)return t;let s=r[e];if(s)return s;let i=r[n];if(i)return readFileSync(i,"utf-8")}function wn(t=process.argv){let e={},n=t.indexOf("--expose");if(n!==-1&&n<t.length-1){let r=t[n+1].split(",").map(s=>s.trim()).filter(s=>s.length>0);e.expose=r.length>0?r:void 0;}return Ns(t,e)}function Ns(t,e){let n=t.indexOf("--webhook-port");n!==-1&&n<t.length-1&&(e.webhookPort=Number.parseInt(t[n+1],10));let r=t.indexOf("--webhook-secret");r!==-1&&r<t.length-1&&(e.webhookSecret=t[r+1]),!e.webhookSecret&&process.env.SAP_WEBHOOK_SECRET&&(e.webhookSecret=process.env.SAP_WEBHOOK_SECRET),e.webhookSecret!==void 0&&e.webhookSecret.trim().length===0&&(e.webhookSecret=void 0);let s=t.indexOf("--webhook-host");if(s!==-1&&s<t.length-1&&(e.webhookHost=t[s+1]),!e.webhookHost&&process.env.SAP_WEBHOOK_HOST&&(e.webhookHost=process.env.SAP_WEBHOOK_HOST),process.env.SAP_WEBHOOK_MAX_BODY_BYTES){let E=Number.parseInt(process.env.SAP_WEBHOOK_MAX_BODY_BYTES,10);Number.isFinite(E)&&E>0&&(e.webhookMaxBodyBytes=E);}let i=t.indexOf("--transport");if(i!==-1&&i<t.length-1){let E=t[i+1];(E==="stdio"||E==="http"||E==="sse")&&(e.transport=E);}if(e.transport===void 0){let E=process.env.SAP_MCP_TRANSPORT?.trim();(E==="stdio"||E==="http"||E==="sse")&&(e.transport=E);}let o=t.indexOf("--port");if(o!==-1&&o<t.length-1&&(e.port=Number.parseInt(t[o+1],10)),e.port===void 0&&process.env.SAP_MCP_PORT){let E=Number.parseInt(process.env.SAP_MCP_PORT,10);Number.isFinite(E)&&E>0&&E<=65535&&(e.port=E);}let a=t.indexOf("--api-key");a!==-1&&a<t.length-1&&(e.apiKey=t[a+1]);let c=["sndpor","sndprt","sndprn","rcvpor","rcvprt","rcvprn"];for(let E of c){let X=t.indexOf(`--${E}`);X!==-1&&X<t.length-1&&(e[E]=t[X+1]);}let l=t.indexOf("--catalog-v2-url");l!==-1&&l<t.length-1&&(e.catalogV2Url=t[l+1]),!e.catalogV2Url&&process.env.ODATA_V2_CATALOG_PATH&&(e.catalogV2Url=process.env.ODATA_V2_CATALOG_PATH);let d=t.indexOf("--catalog-v4-url");d!==-1&&d<t.length-1&&(e.catalogV4Url=t[d+1]);let u=t.includes("--allow-write")||process.env.SAP_MCP_ALLOW_WRITE==="true";e.readOnly=!u,e.allowWriteExplicit=u;let m=t.indexOf("--tool-timeout"),p=m!==-1&&m<t.length-1?t[m+1]:process.env.SAP_MCP_TOOL_TIMEOUT;if(p!==void 0){let E=Number.parseInt(p,10);if(!Number.isFinite(E)||E<0)throw new Error(`--tool-timeout muss eine Zahl in Sekunden sein (0 schaltet das Budget ab), war: "${p}"`);e.toolTimeoutSeconds=E;}let f=t.indexOf("--cache-dir"),g=f!==-1&&f<t.length-1?t[f+1]:process.env.SAP_CACHE_DIR;g&&g.trim().length>0&&(e.cacheDir=g.trim());let h=t.indexOf("--concurrency"),S=h!==-1&&h<t.length-1?t[h+1]:process.env.SAP_CONCURRENCY;if(S!==void 0){let E=Number.parseInt(S,10);if(!Number.isFinite(E)||E<1)throw new Error(`--concurrency muss eine Zahl >= 1 sein, war: "${S}"`);e.concurrency=E;}let T=t.indexOf("--destinations"),y=T!==-1&&T<t.length-1?t[T+1]:process.env.SAP_MCP_DESTINATIONS;y&&y.trim().length>0&&(e.destinationsFile=y.trim());let b=t.indexOf("--admin-token"),D=b!==-1&&b<t.length-1?t[b+1]:process.env.SAP_MCP_ADMIN_TOKEN;if(D&&D.trim().length>0){let E=D.trim();if(E.length<vn)throw new Error(`--admin-token / SAP_MCP_ADMIN_TOKEN muss mindestens ${vn} Zeichen lang sein (z. B. openssl rand -base64 32).`);e.adminToken=E;}(t.includes("--demo")||process.env.SAP_MCP_DEMO==="true")&&(e.demo=true);let ce=t.indexOf("--help"),ze=t.indexOf("-h");(ce!==-1||ze!==-1)&&(e.help=true);let kt=t.indexOf("--tiers");if(kt!==-1&&kt<t.length-1){let E=t[kt+1].split(",").map(X=>X.trim()).filter(X=>X.length>0);e.tiers=E.length>0?E:void 0;}let Ot=t.indexOf("--destination-name");Ot!==-1&&Ot<t.length-1&&(e.destinationName=t[Ot+1]),!e.destinationName&&process.env.SAP_DESTINATION_NAME&&(e.destinationName=process.env.SAP_DESTINATION_NAME);let It=t.indexOf("--destination-service-url");It!==-1&&It<t.length-1&&(e.destinationServiceUrl=t[It+1]),!e.destinationServiceUrl&&process.env.SAP_DESTINATION_SERVICE_URL&&(e.destinationServiceUrl=process.env.SAP_DESTINATION_SERVICE_URL);let Dt=t.indexOf("--issuer");Dt!==-1&&Dt<t.length-1&&(e.issuer=t[Dt+1]),!e.issuer&&process.env.SAP_ISSUER&&(e.issuer=process.env.SAP_ISSUER);let Nt=t.indexOf("--name-id-format");Nt!==-1&&Nt<t.length-1&&(e.nameIdFormat=t[Nt+1]),!e.nameIdFormat&&process.env.SAP_NAME_ID_FORMAT&&(e.nameIdFormat=process.env.SAP_NAME_ID_FORMAT);let xt=t.indexOf("--private-key"),ks=xt!==-1&&xt<t.length-1?t[xt+1]:void 0;e.privateKey=ke(ks,"SAP_PRIVATE_KEY","SAP_PRIVATE_KEY_FILE");let Mt=t.indexOf("--certificate"),Os=Mt!==-1&&Mt<t.length-1?t[Mt+1]:void 0;e.certificate=ke(Os,"SAP_CERTIFICATE","SAP_CERTIFICATE_FILE");let Ut=t.indexOf("--clock-skew");Ut!==-1&&Ut<t.length-1&&(e.clockSkew=Number.parseInt(t[Ut+1],10)),e.clockSkew===void 0&&process.env.SAP_CLOCK_SKEW&&(e.clockSkew=Number.parseInt(process.env.SAP_CLOCK_SKEW,10));let $t=t.indexOf("--assertion-lifetime");return $t!==-1&&$t<t.length-1&&(e.assertionLifetime=Number.parseInt(t[$t+1],10)),e.assertionLifetime===void 0&&process.env.SAP_ASSERTION_LIFETIME&&(e.assertionLifetime=Number.parseInt(process.env.SAP_ASSERTION_LIFETIME,10)),e}function En(t){return !!(t.sndprn&&t.rcvprn)}function J(t,e){return !e||e.length===0?true:e.some(n=>minimatch(t,n))}var Cn=z$1.object({tlsVerify:z$1.boolean().default(true),clientCert:z$1.string().optional(),clientKey:z$1.string().optional(),clientKeyPassphrase:z$1.string().optional(),requestTimeoutMs:z$1.number().int().positive().default(3e4),caCertPath:z$1.string().optional()}).superRefine((t,e)=>{let n=t.clientCert!==void 0,r=t.clientKey!==void 0;n!==r&&e.addIssue({code:"custom",message:n?"SAP_CLIENT_CERT/PATH set without matching SAP_CLIENT_KEY/PATH":"SAP_CLIENT_KEY/PATH set without matching SAP_CLIENT_CERT/PATH",path:["clientKey"]});});function Lt(t=process.env){let e=t.SAP_TLS_VERIFY!=="false",n=t.SAP_CA_CERT?.trim();n&&!t.NODE_EXTRA_CA_CERTS&&(process.env.NODE_EXTRA_CA_CERTS=n);let r=ke(void 0,"SAP_CLIENT_CERT","SAP_CLIENT_CERT_PATH",t),s=ke(void 0,"SAP_CLIENT_KEY","SAP_CLIENT_KEY_PATH",t),i=t.SAP_CLIENT_KEY_PASSPHRASE,o=t.SAP_HTTP_TIMEOUT_MS!==void 0?Number.parseInt(t.SAP_HTTP_TIMEOUT_MS,10):void 0,a={tlsVerify:e,...n?{caCertPath:n}:{},...r!==void 0?{clientCert:r}:{},...s!==void 0?{clientKey:s}:{},...i!==void 0?{clientKeyPassphrase:i}:{},...o!==void 0?{requestTimeoutMs:o}:{}},c=Cn.safeParse(a);if(!c.success){let l=c.error.issues.map(d=>` - ${d.path.join(".")||"root"}: ${d.message}`).join(`
|
|
3
3
|
`);throw new Error(`Invalid HTTP options:
|
|
4
|
-
${l}`)}return c.data}function Ye(t){let e={name:t.name,url:t.baseUrl,sapClient:t.sapClient};switch(t.authType){case "basic":return {...e,auth:{type:"basic",username:t.username,password:t.password}};case "user-basic":throw new Error(`Destination "${t.name}" verlangt eine pers\xF6nliche SAP-Anmeldung (authType user-basic): SAP-Benutzer und -Passwort kommen je Anfrage \xFCber X-SAP-Username/X-SAP-Password \u2014 ohne sie gibt es keinen technischen Fallback.`);case "oauth2":return {...e,auth:{type:"oauth2",clientId:t.clientId,clientSecret:t.clientSecret,tokenServiceUrl:t.tokenServiceUrl}};case "apikey":return {...e,headers:{APIKey:t.apiKey},auth:{type:"apikey",apiKey:t.apiKey}};case "ias":return {...e,auth:{type:"ias",clientId:t.clientId,clientSecret:t.clientSecret,tenantUrl:t.tenantUrl}};case "xsuaa":return {...e,auth:{type:"xsuaa",clientId:t.clientId,clientSecret:t.clientSecret,xsuaaUrl:t.xsuaaUrl}};case "btp-principal":return {...e,auth:{type:"btp-principal",destinationName:t.destinationName,destinationServiceUrl:t.destinationServiceUrl,clientId:t.clientId,clientSecret:t.clientSecret,tokenServiceUrl:t.tokenServiceUrl}};case "saml-bearer":return {...e,auth:{type:"saml-bearer",tokenServiceUrl:t.tokenServiceUrl,clientId:t.clientId,issuer:t.issuer,nameIdFormat:t.nameIdFormat,privateKey:t.privateKey,certificate:t.certificate,clockSkew:t.clockSkew,assertionLifetime:t.assertionLifetime}}}}var de=new AsyncLocalStorage;function Pn(){return randomBytes(6).toString("hex")}function le(){return de.getStore()}function Rn(t,e){return de.run(t,e)}function Xe(){let t=de.getStore();if(t)return t.sapCalls.count+=1,t.correlationId}function _n(t=Date.now()){let e=de.getStore();if(e?.deadline)return Math.max(0,e.deadline-t)}function Je(){return de.getStore()?.signal?.aborted??false}var $=class extends Error{timedOut;constructor(e,n){super(e?`Zeitbudget f\xFCr ${n??"den Tool-Aufruf"} \xFCberschritten \u2014 abgebrochen.`:`Client hat die Verbindung verworfen \u2014 ${n??"der Tool-Aufruf"} wurde abgebrochen.`),this.name="ToolCallAbortedError",this.timedOut=e;}};function Qe(){let t=de.getStore();if(!t?.signal?.aborted)return;let e=t.signal.reason?.name==="TimeoutError";throw new $(e,t.toolName)}function kn(t,e){if(!e||e<=0)return {signal:t,deadline:void 0};let n=AbortSignal.timeout(e),r=Date.now()+e;return t?{signal:AbortSignal.any([t,n]),deadline:r}:{signal:n,deadline:r}}function ue(t){if(t==null)return {code:"UNKNOWN",message:"Unknown SAP error",details:[],rawResponse:t};if(typeof t=="string")return {code:"UNKNOWN",message:t,details:[],rawResponse:t};let n=t.error;if(!n)return {code:"UNKNOWN",message:"Unknown SAP error",details:[],rawResponse:t};let r=n.message,s;r&&typeof r=="object"&&"value"in r?s=r.value??"Unknown error":typeof r=="string"?s=r:s="Unknown error";let a=(n.innererror?.errordetails??[]).map(c=>({code:c.code??"",message:c.message??"",severity:c.severity??"error",target:c.target}));return {code:n.code??"UNKNOWN",message:s,target:n.target,details:a,rawResponse:t}}function Bt(t){if(typeof t!="string")return false;let e=t.trimStart().slice(0,200).toLowerCase();return e.startsWith("<!doctype")||e.startsWith("<html")||e.includes("<title")}function Ft(t){let e=Z(t).toLowerCase();return /anmeldung fehlgeschlagen|anmeldefehler|logon (error|failed)|login failed|unauthori[sz]ed/.test(e)}var Ht="Benutzer und Passwort pr\xFCfen. Ist sap-client gesetzt? Ohne Mandant lehnt das Gateway die Anmeldung ebenfalls ab. Bei pers\xF6nlicher Anmeldung (user-basic): SAP-Benutzer/-Passwort im n8n-Credential pr\xFCfen \u2014 und nicht mehrfach probieren, SAP sperrt die Kennung nach wenigen Fehlversuchen.";function Z(t){let e=/<title[^>]*>([^<]{1,200})<\/title>/i.exec(t)?.[1]?.trim();return e||t.replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<[^>]+>/g," ").replace(/\s+/g," ").trim().slice(0,200)||"HTML-Seite ohne verwertbaren Text"}function $s(t){return /\/IW[A-Z]+\/[A-Z_]+\/\d+/i.exec(t)?.[0]}var Ls=new Set(["UNABLE_TO_GET_ISSUER_CERT_LOCALLY","UNABLE_TO_VERIFY_LEAF_SIGNATURE","SELF_SIGNED_CERT_IN_CHAIN","DEPTH_ZERO_SELF_SIGNED_CERT","CERT_UNTRUSTED"]);function Bs(t){let e=t?.code;if(typeof e=="string")return e;let n=t?.cause;if(typeof n?.code=="string")return n.code}function Fs(t){let n=(t?.cause?.cert??t?.cert)?.issuer?.CN;return typeof n=="string"?n:void 0}function pe(t,e){if(t instanceof $)return {code:t.timedOut?"TOOL_TIMEOUT":"ABORTED",message:t.message};let n=Bs(t);if(n&&Ls.has(n)){let i=Fs(t);return {code:"TLS_CHAIN_INCOMPLETE",message:i?`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar (${n}). Aussteller: ${i}.`:`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar (${n}).`,guidance:"Root-CA \xFCber SAP_CA_CERT=<pfad> einbinden (intern NODE_EXTRA_CA_CERTS). Welches Zertifikat fehlt, zeigt `test-connection` (Stufe TLS) oder `openssl s_client -connect <host>:<port> -showcerts`. SAP_TLS_VERIFY=false ist kein Ersatz \u2014 es schaltet die Pr\xFCfung nur ab."}}if(n==="ENOTFOUND"||n==="EAI_AGAIN")return {code:"DNS_FAILED",message:`Der SAP-Host ist nicht aufl\xF6sbar (${n}).`,guidance:"SAP_BASE_URL pr\xFCfen. Im Container zus\xE4tzlich die DNS-Konfiguration."};if(n==="ECONNREFUSED")return {code:"CONNECTION_REFUSED",message:"Der SAP-Host nimmt keine Verbindung auf dem angegebenen Port an.",guidance:"Port in SAP_BASE_URL pr\xFCfen (HTTPS-Port des ICM, oft 44300 oder 8443)."};if(n==="ECONNABORTED"||n==="ETIMEDOUT")return {code:"TIMEOUT",message:"Das SAP-System hat nicht rechtzeitig geantwortet.",guidance:"Anfrage eingrenzen (top/skip, filter) oder SAP_HTTP_TIMEOUT_MS erh\xF6hen \u2014 aber unterhalb der kleinsten Client-Grenze bleiben."};if(isAxiosError(t)&&t.response)return Hs(t.response.status,t.response.data,e);let r=t;if(typeof r?.message=="string"&&r.message.length>0)return Bt(r.message)?Ft(r.message)?{code:"AUTH_FAILED",message:`Anmeldung abgelehnt (SAP-Anmeldeseite: ${Z(r.message)}).`,guidance:Ht}:{code:"HTML_RESPONSE",message:`SAP hat eine HTML-Seite statt Daten geliefert: ${Z(r.message)}`,guidance:"Meist eine abgelehnte Anmeldung oder ein ung\xFCltiger Pfad. Ist sap-client gesetzt? Ist der Service in /IWFND/MAINT_SERVICE aktiviert?"}:{code:typeof r.code=="string"?r.code:"ERROR",message:r.message};if(t instanceof Error)return Bt(t.message)?Ft(t.message)?{code:"AUTH_FAILED",message:`Anmeldung abgelehnt (SAP-Anmeldeseite: ${Z(t.message)}).`,guidance:Ht}:{code:"HTML_RESPONSE",message:`SAP hat eine HTML-Seite statt Daten geliefert: ${Z(t.message)}`,guidance:"Meist eine abgelehnte Anmeldung oder ein ung\xFCltiger Pfad. Ist sap-client gesetzt? Ist der Service in /IWFND/MAINT_SERVICE aktiviert?"}:{code:"ERROR",message:t.message};if(typeof t=="string")return {code:"ERROR",message:t};let s=t===void 0?void 0:JSON.stringify(t);return {code:"ERROR",message:s?`Unerwarteter Fehler ohne Meldung: ${s}`:"Der Aufruf schlug fehl, ohne einen Fehler zu hinterlassen. Das Serverlog zeigt unter derselben Korrelations-ID, welche SAP-Aufrufe beteiligt waren."}}function Hs(t,e,n){let r=typeof e=="string"?e:JSON.stringify(e??""),s=$s(r);if(s?.toUpperCase().includes("/IWFND/MED/170")){let o=n?.serviceUrl?`Service ${n.serviceUrl}`:"Der Service";return {code:"SERVICE_NOT_REGISTERED",httpStatus:t,sapCode:s,message:`${o} ist im Gateway nicht registriert.`,guidance:"Aktivierung in Transaktion /IWFND/MAINT_SERVICE n\xF6tig. Das ist kein Berechtigungsproblem \u2014 PFCG und SU53 f\xFChren hier nicht weiter."}}if(t===401)return {code:"AUTH_FAILED",httpStatus:401,...s?{sapCode:s}:{},message:"Anmeldung abgelehnt.",guidance:'Ist sap-client gesetzt? Ohne Mandant antwortet das Gateway mit 401 \u201EAnmeldung fehlgeschlagen" statt mit einer Mandantenmeldung. Sonst Benutzer und Passwort pr\xFCfen.'};if(t===403)return {code:"FORBIDDEN",httpStatus:403,...s?{sapCode:s}:{},message:js(e)??"Zugriff verweigert.",guidance:"Berechtigungen in PFCG pr\xFCfen, Trace \xFCber SU53. Bei OData-Services zus\xE4tzlich das Objekt S_SERVICE."};if(t===404)return {code:"NOT_FOUND",httpStatus:404,...s?{sapCode:s}:{},message:n?.serviceUrl?`Unter ${n.serviceUrl} liegt nichts.`:"Der angeforderte Pfad existiert auf diesem System nicht.",guidance:"Pfad \xFCber sap_discover_services pr\xFCfen. Ein Service kann existieren, aber nicht aktiviert sein (/IWFND/MAINT_SERVICE)."};if(Bt(e))return Ft(e)?{code:"AUTH_FAILED",httpStatus:t,message:`Anmeldung abgelehnt (SAP-Anmeldeseite, HTTP ${t}: ${Z(e)}).`,guidance:Ht}:{code:"HTML_RESPONSE",httpStatus:t,message:`SAP hat eine HTML-Fehlerseite geliefert (HTTP ${t}): ${Z(e)}`,guidance:"Der Pfad wird von SAP nicht als OData-Service beantwortet. Bei Katalogpfaden sap_discover_services statt sap_query verwenden."};let i=ue(e);return {code:i.code&&i.code!=="UNKNOWN"?i.code:`HTTP_${t}`,httpStatus:t,...s?{sapCode:s}:{},message:i.message&&i.message!=="Unknown error"&&i.message!=="Unknown SAP error"?i.message:`SAP antwortete mit HTTP ${t} ohne verwertbare Fehlermeldung.`,...i.details.length>0?{guidance:i.details.map(o=>`${o.code}: ${o.message}`).join(" | ")}:{}}}function js(t){let e=ue(t);if(e.message&&!(e.message==="Unknown error"||e.message==="Unknown SAP error"))return e.message}var qs=5e3;function On(t){let e=t?.issuer?.CN??t?.issuer?.O;return Array.isArray(e)?e.join(", ")||void 0:typeof e=="string"?e:void 0}function _(t,e,n,r,s){return {stage:t,status:n,detail:r,durationMs:Date.now()-e,...s?.guidance?{guidance:s.guidance}:{},...s?.info?{info:s.info}:{}}}function Ks(t){try{let e=new URL(t),n=e.protocol==="https:";return {host:e.hostname,port:e.port?Number(e.port):n?443:80,isTls:n}}catch{return}}async function Gs(t,e){let n=Date.now();try{let{address:r,family:s}=await e(t);return _("dns",n,"ok",`${t} l\xF6st auf ${r} auf.`,{info:{address:r,family:s}})}catch(r){let s=pe(r);return _("dns",n,"failed",`${t} ist nicht aufl\xF6sbar.`,{guidance:s.guidance??"SAP_BASE_URL pr\xFCfen. Im Container zus\xE4tzlich die DNS-Konfiguration des Docker-Netzes."})}}async function zs(t,e,n,r,s){let i=Date.now(),o=a=>new Promise((c,l)=>{let d=s({host:t,port:e,rejectUnauthorized:a,servername:t},()=>{let u=d.getPeerCertificate(true);d.destroy(),c(u);});d.setTimeout(r,()=>{d.destroy(),l(Object.assign(new Error(`TLS-Handshake nach ${r} ms ohne Antwort`),{code:"ETIMEDOUT"}));}),d.on("error",u=>{d.destroy(),l(u);});});try{let a=await o(n),c=On(a);return _("tls",i,"ok",n?`TLS-Verbindung steht, Zertifikatskette ist verifizierbar.${c?` Aussteller: ${c}.`:""}`:`TLS-Verbindung steht. Die Zertifikatspr\xFCfung ist \xFCber SAP_TLS_VERIFY=false abgeschaltet \u2014 die Kette wurde NICHT verifiziert.${c?` Aussteller: ${c}.`:""}`,{info:{verified:n,...c?{issuer:c}:{},...a?.subject?.CN?{subject:String(a.subject.CN)}:{},...a?.valid_to?{validTo:a.valid_to}:{}}})}catch(a){let c=pe(a);if(c.code==="TLS_CHAIN_INCOMPLETE"){let l;try{let d=await o(!1);l=On(d);}catch{}return _("tls",i,"failed",l?`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar. Aussteller des Serverzertifikats: ${l}.`:"Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar.",{guidance:c.guidance,...l?{info:{issuer:l}}:{}})}return _("tls",i,"failed",c.message,{guidance:c.guidance})}}async function Ys(t,e){let n=Date.now();try{let r=await t.request({method:"get",url:"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/",headers:{Accept:"application/json"},params:{$format:"json"}});return [_("auth",n,"ok","Anmeldung am Gateway erfolgreich."),_("client",n,"ok",e?`Mandant ${e} wird an jede Anfrage geh\xE4ngt und vom Gateway angenommen.`:"Kein SAP_CLIENT konfiguriert \u2014 das System hat die Anfrage dennoch angenommen. Auf mandantenabh\xE4ngigen Systemen sollte er gesetzt sein.",{info:{sapClient:e??null}}),_("catalog",n,"ok",`Servicekatalog erreichbar (HTTP ${r.status}).`)]}catch(r){let s=pe(r),i=s.httpStatus===401||s.code==="AUTH_FAILED";return i&&!e?[_("auth",n,"ok","Zugangsdaten wurden gesendet."),_("client",n,"failed",'Das Gateway lehnt die Anmeldung ab, und SAP_CLIENT ist nicht gesetzt. Gemessen antwortet das Gateway ohne sap-client mit 401 \u201EAnmeldung fehlgeschlagen" statt mit einer Mandantenmeldung.',{guidance:"SAP_CLIENT setzen (z. B. 100) und erneut pr\xFCfen. Bleibt der 401 bestehen, liegt es an Benutzer oder Passwort."}),_("catalog",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor.")]:i?[_("auth",n,"failed","Anmeldung abgelehnt.",{guidance:`Benutzer und Passwort pr\xFCfen. Mandant ${e} wird gesendet \u2014 bei Zweifeln in SU01 pr\xFCfen, ob der Benutzer in diesem Mandanten existiert und nicht gesperrt ist.`}),_("client",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor."),_("catalog",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor.")]:[_("auth",n,"ok","Anmeldung am Gateway erfolgreich."),_("client",n,"ok",e?`Mandant ${e} angenommen.`:"Kein Mandant konfiguriert."),_("catalog",n,"failed",s.message,{guidance:s.guidance??"Katalogdienst in /IWFND/MAINT_SERVICE pr\xFCfen. Der Server probiert drei bekannte Katalogpfade durch."})]}}async function et(t,e){let n=Date.now(),r=e.budgetMs??qs,s=e.deps?.lookup??lookup,i=e.deps?.connectTls??connect,o=[],a=Ks(e.baseUrl);if(!a)return o.push(_("dns",n,"failed",`SAP_BASE_URL ist keine g\xFCltige URL: "${e.baseUrl}"`,{guidance:"Erwartet wird etwa https://host.example.com:44300 \u2014 mit Schema und ohne Pfad."}),...["tls","auth","client","catalog"].map(c=>_(c,n,"skipped","Nicht gepr\xFCft \u2014 die Basis-URL ist unbrauchbar."))),Ze(o,n);if(o.push(await Gs(a.host,s)),o[0].status==="failed")return o.push(...["tls","auth","client","catalog"].map(c=>_(c,n,"skipped","Nicht gepr\xFCft \u2014 der Name war nicht aufl\xF6sbar."))),Ze(o,n);if(a.isTls){let c=Math.max(500,r-(Date.now()-n));if(o.push(await zs(a.host,a.port,e.tlsVerify!==false,c,i)),o[o.length-1].status==="failed")return o.push(...["auth","client","catalog"].map(l=>_(l,n,"skipped","Nicht gepr\xFCft \u2014 die TLS-Stufe scheiterte zuvor."))),Ze(o,n)}else o.push(_("tls",n,"skipped","Nicht gepr\xFCft \u2014 SAP_BASE_URL verwendet http, nicht https."));return o.push(...await Ys(t,e.sapClient)),Ze(o,n)}function Ze(t,e){let n=t.find(r=>r.status==="failed");return {status:n?"failed":"connected",...n?{failedStage:n.stage}:{},stages:t,durationMs:Date.now()-e,timestamp:new Date().toISOString()}}var me=class{constructor(e,n,r,s=60){this.tokenUrl=e;this.clientId=n;this.clientSecret=r;this.bufferSeconds=s;}cachedToken=null;expiresAt=0;pendingFetch=null;async getToken(e){if(this.cachedToken&&Date.now()<this.expiresAt)return this.cachedToken;if(this.pendingFetch)return this.pendingFetch;this.pendingFetch=this.fetchToken();try{return await this.pendingFetch}finally{this.pendingFetch=null;}}async fetchToken(){let e=await Vt.post(this.tokenUrl,new URLSearchParams({grant_type:"client_credentials"}),{auth:{username:this.clientId,password:this.clientSecret},headers:{"Content-Type":"application/x-www-form-urlencoded"}}),{access_token:n,expires_in:r}=e.data;return this.cachedToken=n,this.expiresAt=Date.now()+(r-this.bufferSeconds)*1e3,n}};function jt(t){return createHash("sha256").update(t).digest("hex")}var Oe=class{constructor(e=100){this.maxSize=e;}cache=new Map;get(e){let n=this.cache.get(e);return n?Date.now()>=n.expiresAt?(this.cache.delete(e),null):(this.cache.delete(e),this.cache.set(e,n),n.token):null}set(e,n,r){if(this.cache.has(e))this.cache.delete(e);else if(this.cache.size>=this.maxSize){let s=this.cache.keys().next().value;s!==void 0&&this.cache.delete(s);}this.cache.set(e,{token:n,expiresAt:r});}},tt=class{clientTokenManager;cache;config;constructor(e){this.config=e,this.clientTokenManager=new me(e.tokenServiceUrl,e.clientId,e.clientSecret),this.cache=new Oe;}async getToken(e){if(!e)return this.clientTokenManager.getToken();let n=jt(e),r=this.cache.get(n);return r||this.exchangeToken(e,n)}async exchangeToken(e,n){let r=await this.clientTokenManager.getToken(),s=`${this.config.destinationServiceUrl}/destination-configuration/v1/destinations/${this.config.destinationName}`,o=(await Vt.get(s,{headers:{Authorization:`Bearer ${r}`,"X-user-token":e}})).data.authTokens;if(!o?.length)throw new Error(`Destination '${this.config.destinationName}': no authTokens in response`);let a=o[0];if(a.error)throw new Error(`Destination '${this.config.destinationName}' token exchange failed: ${a.error}`);let c=parseInt(a.expires_in,10),l=Number.isFinite(c)&&c>0?c:3600,d=Date.now()+(l-60)*1e3;return this.cache.set(n,a.value,d),a.value}};var Ie=class{constructor(e,n){this.config=e;this.discoveryClient=n;this.bufferSeconds=e.bufferSeconds??60;}cachedToken=null;expiresAt=0;pendingFetch=null;jwks=null;bufferSeconds;async getToken(e){if(this.cachedToken&&Date.now()<this.expiresAt)return this.cachedToken;if(this.pendingFetch)return this.pendingFetch;this.pendingFetch=this.fetchAndValidateToken();try{return await this.pendingFetch}finally{this.pendingFetch=null;}}async fetchAndValidateToken(){let e=await this.discoveryClient.discover(this.config.issuerUrl),n=await Vt.post(e.token_endpoint,new URLSearchParams({grant_type:"client_credentials"}),{auth:{username:this.config.clientId,password:this.config.clientSecret},headers:{"Content-Type":"application/x-www-form-urlencoded"}}),{access_token:r,expires_in:s}=n.data;return await this.validateJwt(r,e.jwks_uri,e.issuer),this.cachedToken=r,this.expiresAt=Date.now()+(s-this.bufferSeconds)*1e3,r}async validateJwt(e,n,r){this.jwks||(this.jwks=createRemoteJWKSet(new URL(n)));let{payload:s}=await jwtVerify(e,this.jwks,{issuer:r});return s}};var ee=class{constructor(e=36e5){this.ttlMs=e;}cache=new Map;async discover(e){let n=e.replace(/\/+$/,""),r=this.cache.get(n);if(r&&Date.now()<r.expiresAt)return r.document;let s=`${n}/.well-known/openid-configuration`,o=(await Vt.get(s,{timeout:1e4,headers:{Accept:"application/json"}})).data;if(!o.token_endpoint||!o.jwks_uri)throw new Error(`OIDC discovery document at ${s} is missing required fields (token_endpoint, jwks_uri)`);return this.cache.set(n,{document:o,expiresAt:Date.now()+this.ttlMs}),o}clearCache(){this.cache.clear();}};var ai={email:"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",sapUsername:"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"};function In(t,e){if(!t.includes("-----BEGIN")||!t.includes("-----END"))throw new Error(`Invalid PEM format for ${e}. Expected PEM-encoded content starting with -----BEGIN`)}function ci(t){return `<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="${t.assertionId}" IssueInstant="${t.authnInstant}" Version="2.0"><saml:Issuer>${t.issuer}</saml:Issuer><saml:Subject><saml:NameID Format="${t.nameIdFormat}">${t.nameId}</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="${t.notOnOrAfter}" Recipient="${t.recipient}"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="${t.notBefore}" NotOnOrAfter="${t.notOnOrAfter}"><saml:AudienceRestriction><saml:Audience>${t.audience}</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="${t.authnInstant}"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>`}function di(t,e,n){let r=new SignedXml({privateKey:e,publicCert:n});return r.signatureAlgorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",r.canonicalizationAlgorithm="http://www.w3.org/2001/10/xml-exc-c14n#",r.addReference({xpath:"//*[local-name(.)='Assertion']",digestAlgorithm:"http://www.w3.org/2001/04/xmlenc#sha256",transforms:["http://www.w3.org/2000/09/xmldsig#enveloped-signature","http://www.w3.org/2001/10/xml-exc-c14n#"]}),r.computeSignature(t,{location:{reference:"//*[local-name(.)='Issuer']",action:"after"}}),r.getSignedXml()}var nt=class{cache;config;constructor(e){In(e.privateKey,"private key"),In(e.certificate,"certificate"),this.config={...e,clockSkew:e.clockSkew??120,assertionLifetime:e.assertionLifetime??300},this.cache=new Oe;}async getToken(e){if(!e)throw new Error("saml-bearer auth requires a user token for principal propagation");let n=jt(e),r=this.cache.get(n);return r||this.exchangeToken(e,n)}resolveNameId(e,n){return n==="email"?e.email??e.sub??"":e.sub??""}async exchangeToken(e,n){let r=decodeJwt(e),s=Date.now(),i=this.resolveNameId({sub:r.sub,email:r.email},this.config.nameIdFormat),o=ai[this.config.nameIdFormat],a=new Date(s-this.config.clockSkew*1e3).toISOString(),c=new Date(s+this.config.assertionLifetime*1e3).toISOString(),l=new Date(s).toISOString(),d=ci({assertionId:`_${randomUUID()}`,issuer:this.config.issuer,nameId:i,nameIdFormat:o,recipient:this.config.tokenServiceUrl,audience:this.config.clientId,notBefore:a,notOnOrAfter:c,authnInstant:l}),u=di(d,this.config.privateKey,this.config.certificate),m=Buffer.from(u).toString("base64"),p=await Vt.post(this.config.tokenServiceUrl,new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:saml2-bearer",assertion:m,client_id:this.config.clientId}).toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}}),{access_token:f,expires_in:g}=p.data,h=typeof g=="number"&&g>0?g:3600,S=Date.now()+(h-60)*1e3;return this.cache.set(n,f,S),f}};function Dn(t){if(t.tlsVerify===false||t.clientCert!==void 0)return new Agent({rejectUnauthorized:t.tlsVerify!==false,...t.clientCert?{cert:t.clientCert}:{},...t.clientKey?{key:t.clientKey}:{},...t.clientKeyPassphrase?{passphrase:t.clientKeyPassphrase}:{}})}var pi=3e4,q=ui({name:"sap-http-client",transport:{target:"pino/file",options:{destination:2}}});function mi(t){return t.startsWith("http://")||t.startsWith("https://")}function fi(t){let e=t.replace(/\/+$/,"");try{let n=new URL(e),r=n.pathname.replace(/\/+$/,"");return {origin:`${n.protocol}//${n.host}`,path:r}}catch{return {origin:e,path:""}}}var fe=class{constructor(e,n={}){this.destination=e;switch(this.httpsAgent=Dn(n),this.requestTimeoutMs=n.requestTimeoutMs??pi,e.auth.type){case "oauth2":this.tokenManager=new me(e.auth.tokenServiceUrl,e.auth.clientId,e.auth.clientSecret);break;case "ias":this.tokenManager=new Ie({issuerUrl:e.auth.tenantUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new ee);break;case "xsuaa":this.tokenManager=new Ie({issuerUrl:e.auth.xsuaaUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new ee);break;case "btp-principal":this.tokenManager=new tt({destinationName:e.auth.destinationName,destinationServiceUrl:e.auth.destinationServiceUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret,tokenServiceUrl:e.auth.tokenServiceUrl});break;case "saml-bearer":this.tokenManager=new nt({tokenServiceUrl:e.auth.tokenServiceUrl,clientId:e.auth.clientId,issuer:e.auth.issuer,nameIdFormat:e.auth.nameIdFormat,privateKey:e.auth.privateKey,certificate:e.auth.certificate,clockSkew:e.auth.clockSkew,assertionLifetime:e.auth.assertionLifetime});break;default:this.tokenManager=null;}}tokenManager;userContext=null;httpsAgent;requestTimeoutMs;async request(e){Qe();let n=e.method!=="get",r=le(),s=Xe(),i={...e.params};this.destination.sapClient&&(i["sap-client"]=this.destination.sapClient);let o=e.userContext??this.userContext??void 0,a=await this.buildAuthHeaders(o),c=this.destination.headers??{},l={};if(n){let p=await this.fetchCsrfToken({...c,...a},e.url);p.token&&(l={"x-csrf-token":p.token},p.cookies&&(l.Cookie=p.cookies));}let d={...c,...a,...l,...e.headers},u={method:e.method,url:this.resolveUrl(e.url),data:e.data,headers:d,params:Object.keys(i).length>0?i:void 0,httpsAgent:this.httpsAgent,timeout:this.effectiveTimeoutMs(),signal:r?.signal,beforeRedirect:p=>{this.httpsAgent&&(p.agent=this.httpsAgent);let f=p.headers??{};for(let[g,h]of Object.entries(d))f[g]||(f[g]=h);}},m={...u.headers};m.Authorization&&(m.Authorization=`${m.Authorization.substring(0,15)}***`),q.info({...s?{correlationId:s,tool:r?.toolName}:{},method:u.method,url:u.url,headers:m,params:u.params,hasAuth:!!u.headers.Authorization||!!u.headers.APIKey},"SAP HTTP request outgoing");try{let p=await Vt(u);if(q.info({...s?{correlationId:s}:{},status:p.status,contentType:p.headers["content-type"]},"SAP HTTP response OK"),typeof p.data=="string"&&p.data.trimStart().startsWith("<!DOCTYPE"))throw new Error("SAP returned an HTML page instead of JSON. This usually means authentication failed or the URL path is invalid.");return {data:p.data,headers:p.headers,status:p.status}}catch(p){if(isAxiosError(p)&&p.response){let f=e.expectedStatuses?.includes(p.response.status)??false;(f?q.info.bind(q):q.error.bind(q))({...s?{correlationId:s}:{},status:p.response.status,statusText:p.response.statusText,contentType:p.response.headers?.["content-type"],url:u.url,...f?{expected:true}:{},dataPreview:typeof p.response.data=="string"?p.response.data.substring(0,200):typeof p.response.data},f?"SAP HTTP response (expected)":"SAP HTTP response ERROR");}else p instanceof Error&&q.error({message:p.message,name:p.name},"SAP HTTP connection error");if(n&&this.isCsrf403(p)){Qe();try{let f=await this.fetchCsrfToken({...c,...a},e.url),g={...u.headers};f.token&&(g["x-csrf-token"]=f.token,f.cookies&&(g.Cookie=f.cookies)),Xe();let h=await Vt({...u,headers:g});return {data:h.data,headers:h.headers,status:h.status}}catch(f){throw this.toSapError(f)}}throw this.toSapError(p)}}async buildAuthHeaders(e){switch(this.destination.auth.type){case "basic":return {Authorization:`Basic ${Buffer.from(`${this.destination.auth.username}:${this.destination.auth.password}`).toString("base64")}`};case "saml-bearer":case "btp-principal":case "oauth2":case "ias":case "xsuaa":return {Authorization:`Bearer ${await this.tokenManager.getToken(e?.token)}`};case "apikey":return {APIKey:this.destination.auth.apiKey};default:return {}}}resolveUrl(e){if(mi(e))return e;let{origin:n,path:r}=fi(this.destination.url);return r&&(e===r||e.startsWith(`${r}/`))?`${n}${e}`:`${this.destination.url}${e}`}async fetchCsrfToken(e,n){let r=this.destination.url,s=[];if(n){let i=n.replace(/\/[^/]*$/,"/");i!=="/"&&i!==n&&s.push(this.resolveUrl(i));}s.push(`${r}/`,r);for(let i of s){Qe(),Xe();try{let o={...e,"x-csrf-token":"fetch"},a={};this.destination.sapClient&&(a["sap-client"]=this.destination.sapClient);let c=await Vt.get(i,{headers:o,params:Object.keys(a).length>0?a:void 0,validateStatus:()=>!0,timeout:this.effectiveTimeoutMs(),signal:le()?.signal,httpsAgent:this.httpsAgent,beforeRedirect:d=>{this.httpsAgent&&(d.agent=this.httpsAgent);let u=d.headers??{};for(let[m,p]of Object.entries(o))u[m]||(u[m]=p);}}),l=c.headers["x-csrf-token"];if(q.debug({url:i,status:c.status,hasToken:!!l,tokenValue:l?.substring(0,8)},"CSRF token fetch result"),l&&l.toLowerCase()!=="required"){let u=(c.headers["set-cookie"]??[]).map(m=>m.split(";")[0]).join("; ");return {token:l,cookies:u}}}catch{}}return {token:"",cookies:""}}isCsrf403(e){return isAxiosError(e)&&e.response?.status===403}effectiveTimeoutMs(){let e=_n();return e===void 0?this.requestTimeoutMs:Math.max(1,Math.min(this.requestTimeoutMs,e))}toSapError(e){if(e instanceof $)return {code:e.timedOut?"TOOL_TIMEOUT":"ABORTED",message:e.message,details:[],rawResponse:void 0};if(isAxiosError(e)&&(e.code==="ERR_CANCELED"||e.name==="CanceledError")){let n=le()?.signal?.reason?.name==="TimeoutError";return {code:n?"TOOL_TIMEOUT":"ABORTED",message:n?"Zeitbudget des Tool-Aufrufs \xFCberschritten \u2014 laufender SAP-Aufruf abgebrochen.":"Client hat die Verbindung verworfen \u2014 laufender SAP-Aufruf abgebrochen.",details:[],rawResponse:void 0}}return isAxiosError(e)&&(e.code==="ECONNABORTED"||e.code==="ETIMEDOUT")?{code:"TIMEOUT",message:`SAP request exceeded ${this.requestTimeoutMs} ms (SAP_HTTP_TIMEOUT_MS): ${e.message}`,details:[],rawResponse:void 0}:isAxiosError(e)&&e.response?ue(e.response.data):e instanceof Error?{code:"CONNECTION_ERROR",message:e.message,details:[],rawResponse:void 0}:ue(e)}};function Nn(t,e){e||t.warn({sapClient:null},'SAP_CLIENT ist nicht gesetzt. Auf mandantenabh\xE4ngigen Systemen antwortet das Gateway ohne sap-client mit 401 \u201EAnmeldung fehlgeschlagen" \u2014 was wie ein falsches Passwort aussieht, aber keines ist. Bei Anmeldeproblemen zuerst SAP_CLIENT setzen (z. B. 100).');}async function xn(t,e,n,r){try{let s=new fe(Ye(e),n),i=await et(s,{baseUrl:e.baseUrl,sapClient:e.sapClient,tlsVerify:n.tlsVerify,...r?{deps:r}:{}});for(let o of i.stages){let a={stage:o.stage,status:o.status,durationMs:o.durationMs,...o.info??{},...o.guidance?{guidance:o.guidance}:{}};o.status==="failed"?t.warn(a,`Selbsttest ${o.stage}: ${o.detail}`):t.info(a,`Selbsttest ${o.stage}: ${o.detail}`);}i.status==="connected"?t.info({durationMs:i.durationMs,sapClient:e.sapClient??null},"Selbsttest bestanden: Namensaufl\xF6sung, TLS, Anmeldung, Mandant und Katalog sind in Ordnung"):t.warn({failedStage:i.failedStage,durationMs:i.durationMs},`Selbsttest gescheitert in Stufe \u201E${i.failedStage}". Der Server l\xE4uft weiter \u2014 Tool-Aufrufe werden bis zur Behebung fehlschlagen. Einzelheiten liefert das Tool test-connection.`);}catch(s){t.warn({err:s instanceof Error?s.message:String(s)},"Selbsttest konnte nicht ausgef\xFChrt werden. Der Server l\xE4uft weiter.");}}function te(t){return `/Date(${Date.parse(t.length===10?`${t}T00:00:00Z`:t)})/`}var w=(t,e,n,r=true)=>({name:t,type:"Edm.String",maxLength:e,...n?{label:n}:{},nullable:r}),Ne=(t,e,n=15,r=3)=>({name:t,type:"Edm.Decimal",precision:n,scale:r,...e?{label:e}:{}}),ne=(t,e)=>({name:t,type:"Edm.DateTime",precision:7,...e?{label:e}:{}}),Mn=(t,e)=>({name:t,type:"Edm.Boolean",...e?{label:e}:{}}),Kt="/sap/opu/odata/sap",gi={technicalName:"API_BUSINESS_PARTNER",title:"Business Partner (A2X)",description:"Business partners, customers and suppliers with their addresses.",path:`${Kt}/API_BUSINESS_PARTNER`,namespace:"API_BUSINESS_PARTNER",entityTypes:[{set:"A_BusinessPartner",keys:["BusinessPartner"],autoKey:{prefix:"10",width:7},properties:[w("BusinessPartner",10,"Business Partner",false),w("Customer",10,"Customer"),w("Supplier",10,"Supplier"),w("BusinessPartnerCategory",1,"BP Category"),w("BusinessPartnerFullName",81,"Full Name"),w("BusinessPartnerName",81,"Name"),w("OrganizationBPName1",40,"Organization Name 1"),w("FirstName",40,"First Name"),w("LastName",40,"Last Name"),w("BusinessPartnerGrouping",4,"Grouping"),w("SearchTerm1",20,"Search Term"),w("Industry",4,"Industry"),w("CreatedByUser",12,"Created By"),ne("CreationDate","Created On"),Mn("IsMarkedForArchiving","Archiving Flag")],navigations:[{name:"to_BusinessPartnerAddress",target:"A_BusinessPartnerAddress",multiplicity:"*",join:[["BusinessPartner","BusinessPartner"]]}]},{set:"A_BusinessPartnerAddress",keys:["BusinessPartner","AddressID"],autoKey:{prefix:"2",width:5},properties:[w("BusinessPartner",10,"Business Partner",false),w("AddressID",10,"Address ID",false),ne("ValidityStartDate","Valid From"),ne("ValidityEndDate","Valid To"),w("StreetName",60,"Street"),w("HouseNumber",10,"House Number"),w("PostalCode",10,"Postal Code"),w("CityName",40,"City"),w("Country",3,"Country"),w("Region",3,"Region"),w("Language",2,"Language")],navigations:[{name:"to_BusinessPartner",target:"A_BusinessPartner",multiplicity:"1",join:[["BusinessPartner","BusinessPartner"]]}]}],functionImports:[]},hi={technicalName:"API_SALES_ORDER_SRV",title:"Sales Order (A2X)",description:"Sales orders with items \u2014 read, create (deep insert), change, release.",path:`${Kt}/API_SALES_ORDER_SRV`,namespace:"API_SALES_ORDER_SRV",entityTypes:[{set:"A_SalesOrder",keys:["SalesOrder"],autoKey:{prefix:"45",width:7},properties:[w("SalesOrder",10,"Sales Order",false),w("SalesOrderType",4,"Order Type"),w("SalesOrganization",4,"Sales Organization"),w("DistributionChannel",2,"Distribution Channel"),w("OrganizationDivision",2,"Division"),w("SoldToParty",10,"Sold-to Party"),w("PurchaseOrderByCustomer",35,"Customer Reference"),w("CreatedByUser",12,"Created By"),ne("CreationDate","Created On"),ne("LastChangeDateTime","Last Changed"),ne("RequestedDeliveryDate","Requested Delivery Date"),Ne("TotalNetAmount","Net Value",16,3),w("TransactionCurrency",5,"Currency"),w("OverallSDProcessStatus",1,"Overall Status"),w("OverallDeliveryStatus",1,"Delivery Status")],navigations:[{name:"to_Item",target:"A_SalesOrderItem",multiplicity:"*",join:[["SalesOrder","SalesOrder"]]}]},{set:"A_SalesOrderItem",keys:["SalesOrder","SalesOrderItem"],properties:[w("SalesOrder",10,"Sales Order",false),w("SalesOrderItem",6,"Item",false),w("Material",40,"Material"),w("SalesOrderItemText",40,"Item Description"),w("SalesOrderItemCategory",4,"Item Category"),Ne("RequestedQuantity","Order Quantity",15,3),w("RequestedQuantityUnit",3,"Unit"),Ne("NetAmount","Net Value",16,3),w("TransactionCurrency",5,"Currency"),w("ProductionPlant",4,"Plant")],navigations:[{name:"to_SalesOrder",target:"A_SalesOrder",multiplicity:"1",join:[["SalesOrder","SalesOrder"]]}]}],functionImports:[{name:"ReleaseSalesOrder",httpMethod:"POST",parameters:[{name:"SalesOrder",type:"Edm.String"}],returnType:"API_SALES_ORDER_SRV.A_SalesOrderType",entitySet:"A_SalesOrder"},{name:"GetOpenSalesOrderCount",httpMethod:"GET",parameters:[{name:"SoldToParty",type:"Edm.String",nullable:true}],returnType:"Edm.Int32"}]},yi={technicalName:"API_PRODUCT_SRV",title:"Product Master (A2X)",description:"Products (materials) with descriptions.",path:`${Kt}/API_PRODUCT_SRV`,namespace:"API_PRODUCT_SRV",entityTypes:[{set:"A_Product",keys:["Product"],autoKey:{prefix:"DEMO-",width:8},properties:[w("Product",40,"Product",false),w("ProductType",4,"Product Type"),w("ProductGroup",9,"Product Group"),w("BaseUnit",3,"Base Unit"),Ne("GrossWeight","Gross Weight",13,3),Ne("NetWeight","Net Weight",13,3),w("WeightUnit",3,"Weight Unit"),w("CreatedByUser",12,"Created By"),ne("CreationDate","Created On"),Mn("IsMarkedForDeletion","Deletion Flag")],navigations:[{name:"to_Description",target:"A_ProductDescription",multiplicity:"*",join:[["Product","Product"]]}]},{set:"A_ProductDescription",keys:["Product","Language"],properties:[w("Product",40,"Product",false),w("Language",2,"Language",false),w("ProductDescription",40,"Description")],navigations:[]}],functionImports:[]},rt=[gi,hi,yi],Wt="MCP_DEMO",Si=[["1000001","2","Nordlicht Maschinenbau GmbH","","Hafenstra\xDFe","12","20457","Hamburg","HH","C","MACH","2024-03-04"],["1000002","2","Weberei Hartmann & S\xF6hne KG","","M\xFChlweg","3","91522","Ansbach","BY","C","TEXT","2024-03-11"],["1000003","2","Fr\xE4nkische Pr\xE4zisionstechnik AG","","Industriestra\xDFe","48","90411","N\xFCrnberg","BY","C","MACH","2024-04-02"],["1000004","2","Altm\xFChl Kunststoffwerk GmbH","","Am Kanal","7","91710","Gunzenhausen","BY","C","CHEM","2024-04-19"],["1000005","2","Seenland Logistik GmbH & Co. KG","","Frankenstra\xDFe","101","91781","Wei\xDFenburg","BY","C","TRAN","2024-05-06"],["1000006","2","Brauerei Hopfenkranz OHG","","Brauhausgasse","2","91154","Roth","BY","C","FOOD","2024-05-21"],["1000007","2","Rothsee Elektronik GmbH","","Gewerbering","15","91126","Schwabach","BY","C","ELEC","2024-06-03"],["1000008","2","Hesselberg Metallbau e.K.","","Bahnhofstra\xDFe","33","91717","Wassertr\xFCdingen","BY","C","MACH","2024-06-17"],["1000009","2","Bavaria Sensorik AG","","Technologiepark","9","91058","Erlangen","BY","C","ELEC","2024-07-01"],["1000010","2","Kastner Werkzeugbau GmbH","","Schmiedeweg","21","90762","F\xFCrth","BY","C","MACH","2024-07-15"],["1000011","2","M\xF6belwerkstatt Lindenhof GmbH","","Lindenallee","5","97070","W\xFCrzburg","BY","C","WOOD","2024-08-05"],["1000012","2","Steinbach Automotive GmbH","","Werkstra\xDFe","60","93055","Regensburg","BY","C","AUTO","2024-08-19"],["1000013","2","Tauber Verpackungen GmbH","","Ringstra\xDFe","18","91541","Rothenburg ob der Tauber","BY","C","PACK","2024-09-02"],["1000014","1","Anna","Berger","Rosenweg","4","96047","Bamberg","BY","C","","2024-09-16"],["1000015","1","Markus","Vogel","Am Stadtpark","11","89073","Ulm","BW","C","","2024-10-07"],["1000016","1","Sabine","Kr\xFCger","Elbchaussee","250","22605","Hamburg","HH","C","","2024-10-21"],["1000017","2","Ruhrstahl Handels GmbH","","Kruppstra\xDFe","77","45143","Essen","NW","S","META","2024-02-05"],["1000018","2","Elbe Chemie AG","","Chemiepark","1","01067","Dresden","SN","S","CHEM","2024-02-12"],["1000019","2","NordPack Verpackungssysteme GmbH","","Hansaring","40","28195","Bremen","HB","S","PACK","2024-02-19"],["1000020","2","Donau Kunststoffe GmbH","","Donaustra\xDFe","88","94032","Passau","BY","B","CHEM","2024-02-26"]],Ti=[["PUMP-100","FERT","PUMPS","ST","42.500","39.800","Kreiselpumpe 100 l/min","Centrifugal pump 100 l/min",1890],["MOTOR-15KW","FERT","DRIVES","ST","118.000","112.500","Drehstrommotor 15 kW","Three-phase motor 15 kW",2450],["SENSOR-T200","HAWA","SENSOR","ST","0.180","0.150","Temperatursensor PT100 -50..200 \xB0C","Temperature sensor PT100 -50..200 \xB0C",64.5],["VALVE-DN50","FERT","VALVES","ST","6.400","6.100","Absperrventil DN50 PN16","Shut-off valve DN50 PN16",312],["HOUSING-AL3","HALB","PARTS","ST","2.900","2.700","Aluminiumgeh\xE4use Gr\xF6\xDFe 3","Aluminium housing size 3",148],["PLC-B200","HAWA","CONTROL","ST","1.100","0.950","Steuerung SPS B200","PLC controller B200",985],["CABLE-5M","HAWA","CABLE","ST","0.600","0.550","Anschlusskabel 5 m geschirmt","Connection cable 5 m shielded",27.9],["FILTER-F40","HAWA","FILTER","ST","0.900","0.800","Filtereinsatz F40","Filter cartridge F40",45],["SEAL-KIT-3","HAWA","PARTS","ST","0.250","0.200","Dichtungssatz Baugr\xF6\xDFe 3","Seal kit size 3",38],["BRACKET-XL","HALB","PARTS","ST","3.300","3.100","Wandhalterung XL verzinkt","Wall bracket XL galvanised",72]],Ai=[["4500001","1000001","PO-2026-0117","2026-01-08","2026-01-29","C","C",[["PUMP-100",2],["SEAL-KIT-3",4]]],["4500002","1000003","BEST-88123","2026-01-12","2026-02-05","C","C",[["MOTOR-15KW",1],["CABLE-5M",6],["PLC-B200",1]]],["4500003","1000007","RE-2026-014","2026-01-19","2026-02-10","C","C",[["SENSOR-T200",25],["CABLE-5M",25]]],["4500004","1000004","AK-4471","2026-02-02","2026-02-27","C","C",[["VALVE-DN50",8],["SEAL-KIT-3",8]]],["4500005","1000009","BS-2026-0301","2026-02-16","2026-03-13","C","B",[["SENSOR-T200",120],["HOUSING-AL3",40],["BRACKET-XL",40]]],["4500006","1000012","SA-77201","2026-03-03","2026-03-31","B","B",[["MOTOR-15KW",3],["PLC-B200",3],["CABLE-5M",12]]],["4500007","1000010","KW-2026-19","2026-03-10","2026-04-07","B","A",[["PUMP-100",1],["FILTER-F40",10]]],["4500008","1000005","SL-0209","2026-03-24","2026-04-14","B","A",[["BRACKET-XL",30],["HOUSING-AL3",12]]],["4500009","1000002","WH-2026-04","2026-04-06","2026-05-04","B","A",[["MOTOR-15KW",2],["SENSOR-T200",10]]],["4500010","1000014","","2026-04-14","2026-04-28","C","C",[["FILTER-F40",2],["SEAL-KIT-3",1]]],["4500011","1000011","ML-3350","2026-05-05","2026-06-02","A","A",[["VALVE-DN50",4],["PUMP-100",1],["CABLE-5M",4]]],["4500012","1000013","TV-2026-0888","2026-05-18","2026-06-15","A","A",[["PLC-B200",2],["SENSOR-T200",40],["CABLE-5M",40],["BRACKET-XL",8]]],["4500013","1000015","","2026-06-01","2026-06-22","A","A",[["FILTER-F40",5]]],["4500014","1000006","HK-2026-11","2026-06-15","2026-07-13","A","A",[["PUMP-100",3],["VALVE-DN50",6],["SEAL-KIT-3",6]]],["4500015","1000008","HM-0521","2026-07-01","2026-07-29","A","A",[["MOTOR-15KW",1],["HOUSING-AL3",20],["PLC-B200",1]]]];function qt(t,e=3){return t.toFixed(e)}function Gt(){let t=[],e=[];for(let[c,l,d,u,m,p,f,g,h,S,T,y]of Si){let b=l==="1",D=b?`${d} ${u}`:d;t.push({BusinessPartner:c,Customer:S==="C"||S==="B"?c:"",Supplier:S==="S"||S==="B"?c:"",BusinessPartnerCategory:l,BusinessPartnerFullName:D,BusinessPartnerName:D,OrganizationBPName1:b?"":d,FirstName:b?d:"",LastName:b?u:"",BusinessPartnerGrouping:b?"BP02":"BP01",SearchTerm1:(b?u:d.split(" ")[0]).toUpperCase().slice(0,20),Industry:T,CreatedByUser:Wt,CreationDate:te(y),IsMarkedForArchiving:false}),e.push({BusinessPartner:c,AddressID:String(2e4+t.length),ValidityStartDate:te(y),ValidityEndDate:te("9999-12-31"),StreetName:m,HouseNumber:p,PostalCode:f,CityName:g,Country:"DE",Region:h,Language:"DE"});}let n=[],r=[],s=new Map,i=new Map;Ti.forEach(([c,l,d,u,m,p,f,g,h],S)=>{s.set(c,h),i.set(c,g),n.push({Product:c,ProductType:l,ProductGroup:d,BaseUnit:u,GrossWeight:m,NetWeight:p,WeightUnit:"KG",CreatedByUser:Wt,CreationDate:te(`2023-11-${String(6+S).padStart(2,"0")}`),IsMarkedForDeletion:false}),r.push({Product:c,Language:"DE",ProductDescription:f}),r.push({Product:c,Language:"EN",ProductDescription:g});});let o=[],a=[];for(let[c,l,d,u,m,p,f,g]of Ai){let h=0;g.forEach(([S,T],y)=>{let b=T*(s.get(S)??0);h+=b,a.push({SalesOrder:c,SalesOrderItem:String((y+1)*10),Material:S,SalesOrderItemText:i.get(S)??S,SalesOrderItemCategory:"TAN",RequestedQuantity:qt(T),RequestedQuantityUnit:"ST",NetAmount:qt(b),TransactionCurrency:"EUR",ProductionPlant:"1010"});}),o.push({SalesOrder:c,SalesOrderType:"OR",SalesOrganization:"1010",DistributionChannel:"10",OrganizationDivision:"00",SoldToParty:l,PurchaseOrderByCustomer:d,CreatedByUser:Wt,CreationDate:te(u),LastChangeDateTime:te(`${u}T09:30:00Z`),RequestedDeliveryDate:te(m),TotalNetAmount:qt(h),TransactionCurrency:"EUR",OverallSDProcessStatus:p,OverallDeliveryStatus:f});}return {A_BusinessPartner:t,A_BusinessPartnerAddress:e,A_SalesOrder:o,A_SalesOrderItem:a,A_Product:n,A_ProductDescription:r}}function bi(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function wi(t,e){return `assoc_${t.set}_${e}`}function Un(t){let e=t.namespace,n=[],r=[],s=[];for(let a of t.entityTypes){let c=a.properties.map(d=>{let u=[`Name="${d.name}"`,`Type="${d.type}"`,`Nullable="${d.nullable===false||a.keys.includes(d.name)?"false":"true"}"`];return d.maxLength!==void 0&&u.push(`MaxLength="${d.maxLength}"`),d.precision!==void 0&&u.push(`Precision="${d.precision}"`),d.scale!==void 0&&u.push(`Scale="${d.scale}"`),d.label&&u.push(`sap:label="${bi(d.label)}"`),a.keys.includes(d.name)&&u.push('sap:creatable="true" sap:updatable="false"'),` <Property ${u.join(" ")}/>`}).join(`
|
|
5
|
-
`),l=a.navigations.map(d=>{let u=
|
|
4
|
+
${l}`)}return c.data}function Ye(t){let e={name:t.name,url:t.baseUrl,sapClient:t.sapClient};switch(t.authType){case "basic":return {...e,auth:{type:"basic",username:t.username,password:t.password}};case "user-basic":throw new Error(`Destination "${t.name}" verlangt eine pers\xF6nliche SAP-Anmeldung (authType user-basic): SAP-Benutzer und -Passwort kommen je Anfrage \xFCber X-SAP-Username/X-SAP-Password \u2014 ohne sie gibt es keinen technischen Fallback.`);case "oauth2":return {...e,auth:{type:"oauth2",clientId:t.clientId,clientSecret:t.clientSecret,tokenServiceUrl:t.tokenServiceUrl}};case "apikey":return {...e,headers:{APIKey:t.apiKey},auth:{type:"apikey",apiKey:t.apiKey}};case "ias":return {...e,auth:{type:"ias",clientId:t.clientId,clientSecret:t.clientSecret,tenantUrl:t.tenantUrl}};case "xsuaa":return {...e,auth:{type:"xsuaa",clientId:t.clientId,clientSecret:t.clientSecret,xsuaaUrl:t.xsuaaUrl}};case "btp-principal":return {...e,auth:{type:"btp-principal",destinationName:t.destinationName,destinationServiceUrl:t.destinationServiceUrl,clientId:t.clientId,clientSecret:t.clientSecret,tokenServiceUrl:t.tokenServiceUrl}};case "saml-bearer":return {...e,auth:{type:"saml-bearer",tokenServiceUrl:t.tokenServiceUrl,clientId:t.clientId,issuer:t.issuer,nameIdFormat:t.nameIdFormat,privateKey:t.privateKey,certificate:t.certificate,clockSkew:t.clockSkew,assertionLifetime:t.assertionLifetime}}}}var de=new AsyncLocalStorage;function Pn(){return randomBytes(6).toString("hex")}function le(){return de.getStore()}function Rn(t,e){return de.run(t,e)}function Xe(){let t=de.getStore();if(t)return t.sapCalls.count+=1,t.correlationId}function _n(t=Date.now()){let e=de.getStore();if(e?.deadline)return Math.max(0,e.deadline-t)}function Je(){return de.getStore()?.signal?.aborted??false}var $=class extends Error{timedOut;constructor(e,n){super(e?`Zeitbudget f\xFCr ${n??"den Tool-Aufruf"} \xFCberschritten \u2014 abgebrochen.`:`Client hat die Verbindung verworfen \u2014 ${n??"der Tool-Aufruf"} wurde abgebrochen.`),this.name="ToolCallAbortedError",this.timedOut=e;}};function Qe(){let t=de.getStore();if(!t?.signal?.aborted)return;let e=t.signal.reason?.name==="TimeoutError";throw new $(e,t.toolName)}function kn(t,e){if(!e||e<=0)return {signal:t,deadline:void 0};let n=AbortSignal.timeout(e),r=Date.now()+e;return t?{signal:AbortSignal.any([t,n]),deadline:r}:{signal:n,deadline:r}}function ue(t){if(t==null)return {code:"UNKNOWN",message:"Unknown SAP error",details:[],rawResponse:t};if(typeof t=="string")return {code:"UNKNOWN",message:t,details:[],rawResponse:t};let n=t.error;if(!n)return {code:"UNKNOWN",message:"Unknown SAP error",details:[],rawResponse:t};let r=n.message,s;r&&typeof r=="object"&&"value"in r?s=r.value??"Unknown error":typeof r=="string"?s=r:s="Unknown error";let a=(n.innererror?.errordetails??[]).map(c=>({code:c.code??"",message:c.message??"",severity:c.severity??"error",target:c.target}));return {code:n.code??"UNKNOWN",message:s,target:n.target,details:a,rawResponse:t}}function Bt(t){if(typeof t!="string")return false;let e=t.trimStart().slice(0,200).toLowerCase();return e.startsWith("<!doctype")||e.startsWith("<html")||e.includes("<title")}function Ft(t){let e=Z(t).toLowerCase();return /anmeldung fehlgeschlagen|anmeldefehler|logon (error|failed)|login failed|unauthori[sz]ed/.test(e)}var Ht="Benutzer und Passwort pr\xFCfen. Ist sap-client gesetzt? Ohne Mandant lehnt das Gateway die Anmeldung ebenfalls ab. Bei pers\xF6nlicher Anmeldung (user-basic): SAP-Benutzer/-Passwort im n8n-Credential pr\xFCfen \u2014 und nicht mehrfach probieren, SAP sperrt die Kennung nach wenigen Fehlversuchen.";function Z(t){let e=/<title[^>]*>([^<]{1,200})<\/title>/i.exec(t)?.[1]?.trim();return e||t.replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<[^>]+>/g," ").replace(/\s+/g," ").trim().slice(0,200)||"HTML-Seite ohne verwertbaren Text"}function $s(t){return /\/IW[A-Z]+\/[A-Z_]+\/\d+/i.exec(t)?.[0]}var Ls=new Set(["UNABLE_TO_GET_ISSUER_CERT_LOCALLY","UNABLE_TO_VERIFY_LEAF_SIGNATURE","SELF_SIGNED_CERT_IN_CHAIN","DEPTH_ZERO_SELF_SIGNED_CERT","CERT_UNTRUSTED"]);function Bs(t){let e=t?.code;if(typeof e=="string")return e;let n=t?.cause;if(typeof n?.code=="string")return n.code}function Fs(t){let n=(t?.cause?.cert??t?.cert)?.issuer?.CN;return typeof n=="string"?n:void 0}function pe(t,e){if(t instanceof $)return {code:t.timedOut?"TOOL_TIMEOUT":"ABORTED",message:t.message};let n=Bs(t);if(n&&Ls.has(n)){let i=Fs(t);return {code:"TLS_CHAIN_INCOMPLETE",message:i?`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar (${n}). Aussteller: ${i}.`:`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar (${n}).`,guidance:"Root-CA \xFCber SAP_CA_CERT=<pfad> einbinden (intern NODE_EXTRA_CA_CERTS). Welches Zertifikat fehlt, zeigt `test-connection` (Stufe TLS) oder `openssl s_client -connect <host>:<port> -showcerts`. SAP_TLS_VERIFY=false ist kein Ersatz \u2014 es schaltet die Pr\xFCfung nur ab."}}if(n==="ENOTFOUND"||n==="EAI_AGAIN")return {code:"DNS_FAILED",message:`Der SAP-Host ist nicht aufl\xF6sbar (${n}).`,guidance:"SAP_BASE_URL pr\xFCfen. Im Container zus\xE4tzlich die DNS-Konfiguration."};if(n==="ECONNREFUSED")return {code:"CONNECTION_REFUSED",message:"Der SAP-Host nimmt keine Verbindung auf dem angegebenen Port an.",guidance:"Port in SAP_BASE_URL pr\xFCfen (HTTPS-Port des ICM, oft 44300 oder 8443)."};if(n==="ECONNABORTED"||n==="ETIMEDOUT")return {code:"TIMEOUT",message:"Das SAP-System hat nicht rechtzeitig geantwortet.",guidance:"Anfrage eingrenzen (top/skip, filter) oder SAP_HTTP_TIMEOUT_MS erh\xF6hen \u2014 aber unterhalb der kleinsten Client-Grenze bleiben."};if(isAxiosError(t)&&t.response)return Hs(t.response.status,t.response.data,e);let r=t;if(typeof r?.message=="string"&&r.message.length>0)return Bt(r.message)?Ft(r.message)?{code:"AUTH_FAILED",message:`Anmeldung abgelehnt (SAP-Anmeldeseite: ${Z(r.message)}).`,guidance:Ht}:{code:"HTML_RESPONSE",message:`SAP hat eine HTML-Seite statt Daten geliefert: ${Z(r.message)}`,guidance:"Meist eine abgelehnte Anmeldung oder ein ung\xFCltiger Pfad. Ist sap-client gesetzt? Ist der Service in /IWFND/MAINT_SERVICE aktiviert?"}:{code:typeof r.code=="string"?r.code:"ERROR",message:r.message};if(t instanceof Error)return Bt(t.message)?Ft(t.message)?{code:"AUTH_FAILED",message:`Anmeldung abgelehnt (SAP-Anmeldeseite: ${Z(t.message)}).`,guidance:Ht}:{code:"HTML_RESPONSE",message:`SAP hat eine HTML-Seite statt Daten geliefert: ${Z(t.message)}`,guidance:"Meist eine abgelehnte Anmeldung oder ein ung\xFCltiger Pfad. Ist sap-client gesetzt? Ist der Service in /IWFND/MAINT_SERVICE aktiviert?"}:{code:"ERROR",message:t.message};if(typeof t=="string")return {code:"ERROR",message:t};let s=t===void 0?void 0:JSON.stringify(t);return {code:"ERROR",message:s?`Unerwarteter Fehler ohne Meldung: ${s}`:"Der Aufruf schlug fehl, ohne einen Fehler zu hinterlassen. Das Serverlog zeigt unter derselben Korrelations-ID, welche SAP-Aufrufe beteiligt waren."}}function Hs(t,e,n){let r=typeof e=="string"?e:JSON.stringify(e??""),s=$s(r);if(s?.toUpperCase().includes("/IWFND/MED/170")){let o=n?.serviceUrl?`Service ${n.serviceUrl}`:"Der Service";return {code:"SERVICE_NOT_REGISTERED",httpStatus:t,sapCode:s,message:`${o} ist im Gateway nicht registriert.`,guidance:"Aktivierung in Transaktion /IWFND/MAINT_SERVICE n\xF6tig. Das ist kein Berechtigungsproblem \u2014 PFCG und SU53 f\xFChren hier nicht weiter."}}if(t===401)return {code:"AUTH_FAILED",httpStatus:401,...s?{sapCode:s}:{},message:"Anmeldung abgelehnt.",guidance:'Ist sap-client gesetzt? Ohne Mandant antwortet das Gateway mit 401 \u201EAnmeldung fehlgeschlagen" statt mit einer Mandantenmeldung. Sonst Benutzer und Passwort pr\xFCfen.'};if(t===403)return {code:"FORBIDDEN",httpStatus:403,...s?{sapCode:s}:{},message:js(e)??"Zugriff verweigert.",guidance:"Berechtigungen in PFCG pr\xFCfen, Trace \xFCber SU53. Bei OData-Services zus\xE4tzlich das Objekt S_SERVICE."};if(t===404)return {code:"NOT_FOUND",httpStatus:404,...s?{sapCode:s}:{},message:n?.serviceUrl?`Unter ${n.serviceUrl} liegt nichts.`:"Der angeforderte Pfad existiert auf diesem System nicht.",guidance:"Pfad \xFCber sap_discover_services pr\xFCfen. Ein Service kann existieren, aber nicht aktiviert sein (/IWFND/MAINT_SERVICE)."};if(Bt(e))return Ft(e)?{code:"AUTH_FAILED",httpStatus:t,message:`Anmeldung abgelehnt (SAP-Anmeldeseite, HTTP ${t}: ${Z(e)}).`,guidance:Ht}:{code:"HTML_RESPONSE",httpStatus:t,message:`SAP hat eine HTML-Fehlerseite geliefert (HTTP ${t}): ${Z(e)}`,guidance:"Der Pfad wird von SAP nicht als OData-Service beantwortet. Bei Katalogpfaden sap_discover_services statt sap_query verwenden."};let i=ue(e);return {code:i.code&&i.code!=="UNKNOWN"?i.code:`HTTP_${t}`,httpStatus:t,...s?{sapCode:s}:{},message:i.message&&i.message!=="Unknown error"&&i.message!=="Unknown SAP error"?i.message:`SAP antwortete mit HTTP ${t} ohne verwertbare Fehlermeldung.`,...i.details.length>0?{guidance:i.details.map(o=>`${o.code}: ${o.message}`).join(" | ")}:{}}}function js(t){let e=ue(t);if(e.message&&!(e.message==="Unknown error"||e.message==="Unknown SAP error"))return e.message}var qs=5e3;function On(t){let e=t?.issuer?.CN??t?.issuer?.O;return Array.isArray(e)?e.join(", ")||void 0:typeof e=="string"?e:void 0}function _(t,e,n,r,s){return {stage:t,status:n,detail:r,durationMs:Date.now()-e,...s?.guidance?{guidance:s.guidance}:{},...s?.info?{info:s.info}:{}}}function Ks(t){try{let e=new URL(t),n=e.protocol==="https:";return {host:e.hostname,port:e.port?Number(e.port):n?443:80,isTls:n}}catch{return}}async function Gs(t,e){let n=Date.now();try{let{address:r,family:s}=await e(t);return _("dns",n,"ok",`${t} l\xF6st auf ${r} auf.`,{info:{address:r,family:s}})}catch(r){let s=pe(r);return _("dns",n,"failed",`${t} ist nicht aufl\xF6sbar.`,{guidance:s.guidance??"SAP_BASE_URL pr\xFCfen. Im Container zus\xE4tzlich die DNS-Konfiguration des Docker-Netzes."})}}async function zs(t,e,n,r,s){let i=Date.now(),o=a=>new Promise((c,l)=>{let d=s({host:t,port:e,rejectUnauthorized:a,servername:t},()=>{let u=d.getPeerCertificate(true);d.destroy(),c(u);});d.setTimeout(r,()=>{d.destroy(),l(Object.assign(new Error(`TLS-Handshake nach ${r} ms ohne Antwort`),{code:"ETIMEDOUT"}));}),d.on("error",u=>{d.destroy(),l(u);});});try{let a=await o(n),c=On(a);return _("tls",i,"ok",n?`TLS-Verbindung steht, Zertifikatskette ist verifizierbar.${c?` Aussteller: ${c}.`:""}`:`TLS-Verbindung steht. Die Zertifikatspr\xFCfung ist \xFCber SAP_TLS_VERIFY=false abgeschaltet \u2014 die Kette wurde NICHT verifiziert.${c?` Aussteller: ${c}.`:""}`,{info:{verified:n,...c?{issuer:c}:{},...a?.subject?.CN?{subject:String(a.subject.CN)}:{},...a?.valid_to?{validTo:a.valid_to}:{}}})}catch(a){let c=pe(a);if(c.code==="TLS_CHAIN_INCOMPLETE"){let l;try{let d=await o(!1);l=On(d);}catch{}return _("tls",i,"failed",l?`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar. Aussteller des Serverzertifikats: ${l}.`:"Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar.",{guidance:c.guidance,...l?{info:{issuer:l}}:{}})}return _("tls",i,"failed",c.message,{guidance:c.guidance})}}async function Ys(t,e){let n=Date.now();try{let r=await t.request({method:"get",url:"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/",headers:{Accept:"application/json"},params:{$format:"json"}});return [_("auth",n,"ok","Anmeldung am Gateway erfolgreich."),_("client",n,"ok",e?`Mandant ${e} wird an jede Anfrage geh\xE4ngt und vom Gateway angenommen.`:"Kein SAP_CLIENT konfiguriert \u2014 das System hat die Anfrage dennoch angenommen. Auf mandantenabh\xE4ngigen Systemen sollte er gesetzt sein.",{info:{sapClient:e??null}}),_("catalog",n,"ok",`Servicekatalog erreichbar (HTTP ${r.status}).`)]}catch(r){let s=pe(r),i=s.httpStatus===401||s.code==="AUTH_FAILED";return i&&!e?[_("auth",n,"ok","Zugangsdaten wurden gesendet."),_("client",n,"failed",'Das Gateway lehnt die Anmeldung ab, und SAP_CLIENT ist nicht gesetzt. Gemessen antwortet das Gateway ohne sap-client mit 401 \u201EAnmeldung fehlgeschlagen" statt mit einer Mandantenmeldung.',{guidance:"SAP_CLIENT setzen (z. B. 100) und erneut pr\xFCfen. Bleibt der 401 bestehen, liegt es an Benutzer oder Passwort."}),_("catalog",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor.")]:i?[_("auth",n,"failed","Anmeldung abgelehnt.",{guidance:`Benutzer und Passwort pr\xFCfen. Mandant ${e} wird gesendet \u2014 bei Zweifeln in SU01 pr\xFCfen, ob der Benutzer in diesem Mandanten existiert und nicht gesperrt ist.`}),_("client",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor."),_("catalog",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor.")]:[_("auth",n,"ok","Anmeldung am Gateway erfolgreich."),_("client",n,"ok",e?`Mandant ${e} angenommen.`:"Kein Mandant konfiguriert."),_("catalog",n,"failed",s.message,{guidance:s.guidance??"Katalogdienst in /IWFND/MAINT_SERVICE pr\xFCfen. Der Server probiert drei bekannte Katalogpfade durch."})]}}async function et(t,e){let n=Date.now(),r=e.budgetMs??qs,s=e.deps?.lookup??lookup,i=e.deps?.connectTls??connect,o=[],a=Ks(e.baseUrl);if(!a)return o.push(_("dns",n,"failed",`SAP_BASE_URL ist keine g\xFCltige URL: "${e.baseUrl}"`,{guidance:"Erwartet wird etwa https://host.example.com:44300 \u2014 mit Schema und ohne Pfad."}),...["tls","auth","client","catalog"].map(c=>_(c,n,"skipped","Nicht gepr\xFCft \u2014 die Basis-URL ist unbrauchbar."))),Ze(o,n);if(o.push(await Gs(a.host,s)),o[0].status==="failed")return o.push(...["tls","auth","client","catalog"].map(c=>_(c,n,"skipped","Nicht gepr\xFCft \u2014 der Name war nicht aufl\xF6sbar."))),Ze(o,n);if(a.isTls){let c=Math.max(500,r-(Date.now()-n));if(o.push(await zs(a.host,a.port,e.tlsVerify!==false,c,i)),o[o.length-1].status==="failed")return o.push(...["auth","client","catalog"].map(l=>_(l,n,"skipped","Nicht gepr\xFCft \u2014 die TLS-Stufe scheiterte zuvor."))),Ze(o,n)}else o.push(_("tls",n,"skipped","Nicht gepr\xFCft \u2014 SAP_BASE_URL verwendet http, nicht https."));return o.push(...await Ys(t,e.sapClient)),Ze(o,n)}function Ze(t,e){let n=t.find(r=>r.status==="failed");return {status:n?"failed":"connected",...n?{failedStage:n.stage}:{},stages:t,durationMs:Date.now()-e,timestamp:new Date().toISOString()}}var me=class{constructor(e,n,r,s=60){this.tokenUrl=e;this.clientId=n;this.clientSecret=r;this.bufferSeconds=s;}cachedToken=null;expiresAt=0;pendingFetch=null;async getToken(e){if(this.cachedToken&&Date.now()<this.expiresAt)return this.cachedToken;if(this.pendingFetch)return this.pendingFetch;this.pendingFetch=this.fetchToken();try{return await this.pendingFetch}finally{this.pendingFetch=null;}}async fetchToken(){let e=await Vt.post(this.tokenUrl,new URLSearchParams({grant_type:"client_credentials"}),{auth:{username:this.clientId,password:this.clientSecret},headers:{"Content-Type":"application/x-www-form-urlencoded"}}),{access_token:n,expires_in:r}=e.data;return this.cachedToken=n,this.expiresAt=Date.now()+(r-this.bufferSeconds)*1e3,n}};function jt(t){return createHash("sha256").update(t).digest("hex")}var Oe=class{constructor(e=100){this.maxSize=e;}cache=new Map;get(e){let n=this.cache.get(e);return n?Date.now()>=n.expiresAt?(this.cache.delete(e),null):(this.cache.delete(e),this.cache.set(e,n),n.token):null}set(e,n,r){if(this.cache.has(e))this.cache.delete(e);else if(this.cache.size>=this.maxSize){let s=this.cache.keys().next().value;s!==void 0&&this.cache.delete(s);}this.cache.set(e,{token:n,expiresAt:r});}},tt=class{clientTokenManager;cache;config;constructor(e){this.config=e,this.clientTokenManager=new me(e.tokenServiceUrl,e.clientId,e.clientSecret),this.cache=new Oe;}async getToken(e){if(!e)return this.clientTokenManager.getToken();let n=jt(e),r=this.cache.get(n);return r||this.exchangeToken(e,n)}async exchangeToken(e,n){let r=await this.clientTokenManager.getToken(),s=`${this.config.destinationServiceUrl}/destination-configuration/v1/destinations/${this.config.destinationName}`,o=(await Vt.get(s,{headers:{Authorization:`Bearer ${r}`,"X-user-token":e}})).data.authTokens;if(!o?.length)throw new Error(`Destination '${this.config.destinationName}': no authTokens in response`);let a=o[0];if(a.error)throw new Error(`Destination '${this.config.destinationName}' token exchange failed: ${a.error}`);let c=parseInt(a.expires_in,10),l=Number.isFinite(c)&&c>0?c:3600,d=Date.now()+(l-60)*1e3;return this.cache.set(n,a.value,d),a.value}};var Ie=class{constructor(e,n){this.config=e;this.discoveryClient=n;this.bufferSeconds=e.bufferSeconds??60;}cachedToken=null;expiresAt=0;pendingFetch=null;jwks=null;bufferSeconds;async getToken(e){if(this.cachedToken&&Date.now()<this.expiresAt)return this.cachedToken;if(this.pendingFetch)return this.pendingFetch;this.pendingFetch=this.fetchAndValidateToken();try{return await this.pendingFetch}finally{this.pendingFetch=null;}}async fetchAndValidateToken(){let e=await this.discoveryClient.discover(this.config.issuerUrl),n=await Vt.post(e.token_endpoint,new URLSearchParams({grant_type:"client_credentials"}),{auth:{username:this.config.clientId,password:this.config.clientSecret},headers:{"Content-Type":"application/x-www-form-urlencoded"}}),{access_token:r,expires_in:s}=n.data;return await this.validateJwt(r,e.jwks_uri,e.issuer),this.cachedToken=r,this.expiresAt=Date.now()+(s-this.bufferSeconds)*1e3,r}async validateJwt(e,n,r){this.jwks||(this.jwks=createRemoteJWKSet(new URL(n)));let{payload:s}=await jwtVerify(e,this.jwks,{issuer:r});return s}};var ee=class{constructor(e=36e5){this.ttlMs=e;}cache=new Map;async discover(e){let n=e.replace(/\/+$/,""),r=this.cache.get(n);if(r&&Date.now()<r.expiresAt)return r.document;let s=`${n}/.well-known/openid-configuration`,o=(await Vt.get(s,{timeout:1e4,headers:{Accept:"application/json"}})).data;if(!o.token_endpoint||!o.jwks_uri)throw new Error(`OIDC discovery document at ${s} is missing required fields (token_endpoint, jwks_uri)`);return this.cache.set(n,{document:o,expiresAt:Date.now()+this.ttlMs}),o}clearCache(){this.cache.clear();}};var ai={email:"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",sapUsername:"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"};function In(t,e){if(!t.includes("-----BEGIN")||!t.includes("-----END"))throw new Error(`Invalid PEM format for ${e}. Expected PEM-encoded content starting with -----BEGIN`)}function ci(t){return `<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="${t.assertionId}" IssueInstant="${t.authnInstant}" Version="2.0"><saml:Issuer>${t.issuer}</saml:Issuer><saml:Subject><saml:NameID Format="${t.nameIdFormat}">${t.nameId}</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="${t.notOnOrAfter}" Recipient="${t.recipient}"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="${t.notBefore}" NotOnOrAfter="${t.notOnOrAfter}"><saml:AudienceRestriction><saml:Audience>${t.audience}</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="${t.authnInstant}"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>`}function di(t,e,n){let r=new SignedXml({privateKey:e,publicCert:n});return r.signatureAlgorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",r.canonicalizationAlgorithm="http://www.w3.org/2001/10/xml-exc-c14n#",r.addReference({xpath:"//*[local-name(.)='Assertion']",digestAlgorithm:"http://www.w3.org/2001/04/xmlenc#sha256",transforms:["http://www.w3.org/2000/09/xmldsig#enveloped-signature","http://www.w3.org/2001/10/xml-exc-c14n#"]}),r.computeSignature(t,{location:{reference:"//*[local-name(.)='Issuer']",action:"after"}}),r.getSignedXml()}var nt=class{cache;config;constructor(e){In(e.privateKey,"private key"),In(e.certificate,"certificate"),this.config={...e,clockSkew:e.clockSkew??120,assertionLifetime:e.assertionLifetime??300},this.cache=new Oe;}async getToken(e){if(!e)throw new Error("saml-bearer auth requires a user token for principal propagation");let n=jt(e),r=this.cache.get(n);return r||this.exchangeToken(e,n)}resolveNameId(e,n){return n==="email"?e.email??e.sub??"":e.sub??""}async exchangeToken(e,n){let r=decodeJwt(e),s=Date.now(),i=this.resolveNameId({sub:r.sub,email:r.email},this.config.nameIdFormat),o=ai[this.config.nameIdFormat],a=new Date(s-this.config.clockSkew*1e3).toISOString(),c=new Date(s+this.config.assertionLifetime*1e3).toISOString(),l=new Date(s).toISOString(),d=ci({assertionId:`_${randomUUID()}`,issuer:this.config.issuer,nameId:i,nameIdFormat:o,recipient:this.config.tokenServiceUrl,audience:this.config.clientId,notBefore:a,notOnOrAfter:c,authnInstant:l}),u=di(d,this.config.privateKey,this.config.certificate),m=Buffer.from(u).toString("base64"),p=await Vt.post(this.config.tokenServiceUrl,new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:saml2-bearer",assertion:m,client_id:this.config.clientId}).toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}}),{access_token:f,expires_in:g}=p.data,h=typeof g=="number"&&g>0?g:3600,S=Date.now()+(h-60)*1e3;return this.cache.set(n,f,S),f}};function Dn(t){if(t.tlsVerify===false||t.clientCert!==void 0)return new Agent({rejectUnauthorized:t.tlsVerify!==false,...t.clientCert?{cert:t.clientCert}:{},...t.clientKey?{key:t.clientKey}:{},...t.clientKeyPassphrase?{passphrase:t.clientKeyPassphrase}:{}})}var pi=3e4,q=ui({name:"sap-http-client",transport:{target:"pino/file",options:{destination:2}}});function mi(t){return t.startsWith("http://")||t.startsWith("https://")}function fi(t){let e=t.replace(/\/+$/,"");try{let n=new URL(e),r=n.pathname.replace(/\/+$/,"");return {origin:`${n.protocol}//${n.host}`,path:r}}catch{return {origin:e,path:""}}}var fe=class{constructor(e,n={}){this.destination=e;switch(this.httpsAgent=Dn(n),this.requestTimeoutMs=n.requestTimeoutMs??pi,e.auth.type){case "oauth2":this.tokenManager=new me(e.auth.tokenServiceUrl,e.auth.clientId,e.auth.clientSecret);break;case "ias":this.tokenManager=new Ie({issuerUrl:e.auth.tenantUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new ee);break;case "xsuaa":this.tokenManager=new Ie({issuerUrl:e.auth.xsuaaUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new ee);break;case "btp-principal":this.tokenManager=new tt({destinationName:e.auth.destinationName,destinationServiceUrl:e.auth.destinationServiceUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret,tokenServiceUrl:e.auth.tokenServiceUrl});break;case "saml-bearer":this.tokenManager=new nt({tokenServiceUrl:e.auth.tokenServiceUrl,clientId:e.auth.clientId,issuer:e.auth.issuer,nameIdFormat:e.auth.nameIdFormat,privateKey:e.auth.privateKey,certificate:e.auth.certificate,clockSkew:e.auth.clockSkew,assertionLifetime:e.auth.assertionLifetime});break;default:this.tokenManager=null;}}tokenManager;userContext=null;httpsAgent;requestTimeoutMs;async request(e){Qe();let n=e.method!=="get",r=le(),s=Xe(),i={...e.params};this.destination.sapClient&&(i["sap-client"]=this.destination.sapClient);let o=e.userContext??this.userContext??void 0,a=await this.buildAuthHeaders(o),c=this.destination.headers??{},l={};if(n){let p=await this.fetchCsrfToken({...c,...a},e.url);p.token&&(l={"x-csrf-token":p.token},p.cookies&&(l.Cookie=p.cookies));}let d={...c,...a,...l,...e.headers},u={method:e.method,url:this.resolveUrl(e.url),data:e.data,headers:d,params:Object.keys(i).length>0?i:void 0,httpsAgent:this.httpsAgent,timeout:this.effectiveTimeoutMs(),signal:r?.signal,beforeRedirect:p=>{this.httpsAgent&&(p.agent=this.httpsAgent);let f=p.headers??{};for(let[g,h]of Object.entries(d))f[g]||(f[g]=h);}},m={...u.headers};m.Authorization&&(m.Authorization=`${m.Authorization.substring(0,15)}***`),q.info({...s?{correlationId:s,tool:r?.toolName}:{},method:u.method,url:u.url,headers:m,params:u.params,hasAuth:!!u.headers.Authorization||!!u.headers.APIKey},"SAP HTTP request outgoing");try{let p=await Vt(u);if(q.info({...s?{correlationId:s}:{},status:p.status,contentType:p.headers["content-type"]},"SAP HTTP response OK"),typeof p.data=="string"&&p.data.trimStart().startsWith("<!DOCTYPE"))throw new Error("SAP returned an HTML page instead of JSON. This usually means authentication failed or the URL path is invalid.");return {data:p.data,headers:p.headers,status:p.status}}catch(p){if(isAxiosError(p)&&p.response){let f=e.expectedStatuses?.includes(p.response.status)??false;(f?q.info.bind(q):q.error.bind(q))({...s?{correlationId:s}:{},status:p.response.status,statusText:p.response.statusText,contentType:p.response.headers?.["content-type"],url:u.url,...f?{expected:true}:{},dataPreview:typeof p.response.data=="string"?p.response.data.substring(0,200):typeof p.response.data},f?"SAP HTTP response (expected)":"SAP HTTP response ERROR");}else p instanceof Error&&q.error({message:p.message,name:p.name},"SAP HTTP connection error");if(n&&this.isCsrf403(p)){Qe();try{let f=await this.fetchCsrfToken({...c,...a},e.url),g={...u.headers};f.token&&(g["x-csrf-token"]=f.token,f.cookies&&(g.Cookie=f.cookies)),Xe();let h=await Vt({...u,headers:g});return {data:h.data,headers:h.headers,status:h.status}}catch(f){throw this.toSapError(f)}}throw this.toSapError(p)}}async buildAuthHeaders(e){switch(this.destination.auth.type){case "basic":return {Authorization:`Basic ${Buffer.from(`${this.destination.auth.username}:${this.destination.auth.password}`).toString("base64")}`};case "saml-bearer":case "btp-principal":case "oauth2":case "ias":case "xsuaa":return {Authorization:`Bearer ${await this.tokenManager.getToken(e?.token)}`};case "apikey":return {APIKey:this.destination.auth.apiKey};default:return {}}}resolveUrl(e){if(mi(e))return e;let{origin:n,path:r}=fi(this.destination.url);return r&&(e===r||e.startsWith(`${r}/`))?`${n}${e}`:`${this.destination.url}${e}`}async fetchCsrfToken(e,n){let r=this.destination.url,s=[];if(n){let i=n.replace(/\/[^/]*$/,"/");i!=="/"&&i!==n&&s.push(this.resolveUrl(i));}s.push(`${r}/`,r);for(let i of s){Qe(),Xe();try{let o={...e,"x-csrf-token":"fetch"},a={};this.destination.sapClient&&(a["sap-client"]=this.destination.sapClient);let c=await Vt.get(i,{headers:o,params:Object.keys(a).length>0?a:void 0,validateStatus:()=>!0,timeout:this.effectiveTimeoutMs(),signal:le()?.signal,httpsAgent:this.httpsAgent,beforeRedirect:d=>{this.httpsAgent&&(d.agent=this.httpsAgent);let u=d.headers??{};for(let[m,p]of Object.entries(o))u[m]||(u[m]=p);}}),l=c.headers["x-csrf-token"];if(q.debug({url:i,status:c.status,hasToken:!!l,tokenValue:l?.substring(0,8)},"CSRF token fetch result"),l&&l.toLowerCase()!=="required"){let u=(c.headers["set-cookie"]??[]).map(m=>m.split(";")[0]).join("; ");return {token:l,cookies:u}}}catch{}}return {token:"",cookies:""}}isCsrf403(e){return isAxiosError(e)&&e.response?.status===403}effectiveTimeoutMs(){let e=_n();return e===void 0?this.requestTimeoutMs:Math.max(1,Math.min(this.requestTimeoutMs,e))}toSapError(e){if(e instanceof $)return {code:e.timedOut?"TOOL_TIMEOUT":"ABORTED",message:e.message,details:[],rawResponse:void 0};if(isAxiosError(e)&&(e.code==="ERR_CANCELED"||e.name==="CanceledError")){let n=le()?.signal?.reason?.name==="TimeoutError";return {code:n?"TOOL_TIMEOUT":"ABORTED",message:n?"Zeitbudget des Tool-Aufrufs \xFCberschritten \u2014 laufender SAP-Aufruf abgebrochen.":"Client hat die Verbindung verworfen \u2014 laufender SAP-Aufruf abgebrochen.",details:[],rawResponse:void 0}}return isAxiosError(e)&&(e.code==="ECONNABORTED"||e.code==="ETIMEDOUT")?{code:"TIMEOUT",message:`SAP request exceeded ${this.requestTimeoutMs} ms (SAP_HTTP_TIMEOUT_MS): ${e.message}`,details:[],rawResponse:void 0}:isAxiosError(e)&&e.response?ue(e.response.data):e instanceof Error?{code:"CONNECTION_ERROR",message:e.message,details:[],rawResponse:void 0}:ue(e)}};function Nn(t,e){e||t.warn({sapClient:null},'SAP_CLIENT ist nicht gesetzt. Auf mandantenabh\xE4ngigen Systemen antwortet das Gateway ohne sap-client mit 401 \u201EAnmeldung fehlgeschlagen" \u2014 was wie ein falsches Passwort aussieht, aber keines ist. Bei Anmeldeproblemen zuerst SAP_CLIENT setzen (z. B. 100).');}async function xn(t,e,n,r){try{let s=new fe(Ye(e),n),i=await et(s,{baseUrl:e.baseUrl,sapClient:e.sapClient,tlsVerify:n.tlsVerify,...r?{deps:r}:{}});for(let o of i.stages){let a={stage:o.stage,status:o.status,durationMs:o.durationMs,...o.info??{},...o.guidance?{guidance:o.guidance}:{}};o.status==="failed"?t.warn(a,`Selbsttest ${o.stage}: ${o.detail}`):t.info(a,`Selbsttest ${o.stage}: ${o.detail}`);}i.status==="connected"?t.info({durationMs:i.durationMs,sapClient:e.sapClient??null},"Selbsttest bestanden: Namensaufl\xF6sung, TLS, Anmeldung, Mandant und Katalog sind in Ordnung"):t.warn({failedStage:i.failedStage,durationMs:i.durationMs},`Selbsttest gescheitert in Stufe \u201E${i.failedStage}". Der Server l\xE4uft weiter \u2014 Tool-Aufrufe werden bis zur Behebung fehlschlagen. Einzelheiten liefert das Tool test-connection.`);}catch(s){t.warn({err:s instanceof Error?s.message:String(s)},"Selbsttest konnte nicht ausgef\xFChrt werden. Der Server l\xE4uft weiter.");}}function te(t){return `/Date(${Date.parse(t.length===10?`${t}T00:00:00Z`:t)})/`}var v=(t,e,n,r=true)=>({name:t,type:"Edm.String",maxLength:e,...n?{label:n}:{},nullable:r}),Ne=(t,e,n=15,r=3)=>({name:t,type:"Edm.Decimal",precision:n,scale:r,...e?{label:e}:{}}),ne=(t,e)=>({name:t,type:"Edm.DateTime",precision:7,...e?{label:e}:{}}),Mn=(t,e)=>({name:t,type:"Edm.Boolean",...e?{label:e}:{}}),Kt="/sap/opu/odata/sap",gi={technicalName:"API_BUSINESS_PARTNER",title:"Business Partner (A2X)",description:"Business partners, customers and suppliers with their addresses.",path:`${Kt}/API_BUSINESS_PARTNER`,namespace:"API_BUSINESS_PARTNER",entityTypes:[{set:"A_BusinessPartner",keys:["BusinessPartner"],autoKey:{prefix:"10",width:7},properties:[v("BusinessPartner",10,"Business Partner",false),v("Customer",10,"Customer"),v("Supplier",10,"Supplier"),v("BusinessPartnerCategory",1,"BP Category"),v("BusinessPartnerFullName",81,"Full Name"),v("BusinessPartnerName",81,"Name"),v("OrganizationBPName1",40,"Organization Name 1"),v("FirstName",40,"First Name"),v("LastName",40,"Last Name"),v("BusinessPartnerGrouping",4,"Grouping"),v("SearchTerm1",20,"Search Term"),v("Industry",4,"Industry"),v("CreatedByUser",12,"Created By"),ne("CreationDate","Created On"),Mn("IsMarkedForArchiving","Archiving Flag")],navigations:[{name:"to_BusinessPartnerAddress",target:"A_BusinessPartnerAddress",multiplicity:"*",join:[["BusinessPartner","BusinessPartner"]]}]},{set:"A_BusinessPartnerAddress",keys:["BusinessPartner","AddressID"],autoKey:{prefix:"2",width:5},properties:[v("BusinessPartner",10,"Business Partner",false),v("AddressID",10,"Address ID",false),ne("ValidityStartDate","Valid From"),ne("ValidityEndDate","Valid To"),v("StreetName",60,"Street"),v("HouseNumber",10,"House Number"),v("PostalCode",10,"Postal Code"),v("CityName",40,"City"),v("Country",3,"Country"),v("Region",3,"Region"),v("Language",2,"Language")],navigations:[{name:"to_BusinessPartner",target:"A_BusinessPartner",multiplicity:"1",join:[["BusinessPartner","BusinessPartner"]]}]}],functionImports:[]},hi={technicalName:"API_SALES_ORDER_SRV",title:"Sales Order (A2X)",description:"Sales orders with items \u2014 read, create (deep insert), change, release.",path:`${Kt}/API_SALES_ORDER_SRV`,namespace:"API_SALES_ORDER_SRV",entityTypes:[{set:"A_SalesOrder",keys:["SalesOrder"],autoKey:{prefix:"45",width:7},properties:[v("SalesOrder",10,"Sales Order",false),v("SalesOrderType",4,"Order Type"),v("SalesOrganization",4,"Sales Organization"),v("DistributionChannel",2,"Distribution Channel"),v("OrganizationDivision",2,"Division"),v("SoldToParty",10,"Sold-to Party"),v("PurchaseOrderByCustomer",35,"Customer Reference"),v("CreatedByUser",12,"Created By"),ne("CreationDate","Created On"),ne("LastChangeDateTime","Last Changed"),ne("RequestedDeliveryDate","Requested Delivery Date"),Ne("TotalNetAmount","Net Value",16,3),v("TransactionCurrency",5,"Currency"),v("OverallSDProcessStatus",1,"Overall Status"),v("OverallDeliveryStatus",1,"Delivery Status")],navigations:[{name:"to_Item",target:"A_SalesOrderItem",multiplicity:"*",join:[["SalesOrder","SalesOrder"]]}]},{set:"A_SalesOrderItem",keys:["SalesOrder","SalesOrderItem"],properties:[v("SalesOrder",10,"Sales Order",false),v("SalesOrderItem",6,"Item",false),v("Material",40,"Material"),v("SalesOrderItemText",40,"Item Description"),v("SalesOrderItemCategory",4,"Item Category"),Ne("RequestedQuantity","Order Quantity",15,3),v("RequestedQuantityUnit",3,"Unit"),Ne("NetAmount","Net Value",16,3),v("TransactionCurrency",5,"Currency"),v("ProductionPlant",4,"Plant")],navigations:[{name:"to_SalesOrder",target:"A_SalesOrder",multiplicity:"1",join:[["SalesOrder","SalesOrder"]]}]}],functionImports:[{name:"ReleaseSalesOrder",httpMethod:"POST",parameters:[{name:"SalesOrder",type:"Edm.String"}],returnType:"API_SALES_ORDER_SRV.A_SalesOrderType",entitySet:"A_SalesOrder"},{name:"GetOpenSalesOrderCount",httpMethod:"GET",parameters:[{name:"SoldToParty",type:"Edm.String",nullable:true}],returnType:"Edm.Int32"}]},yi={technicalName:"API_PRODUCT_SRV",title:"Product Master (A2X)",description:"Products (materials) with descriptions.",path:`${Kt}/API_PRODUCT_SRV`,namespace:"API_PRODUCT_SRV",entityTypes:[{set:"A_Product",keys:["Product"],autoKey:{prefix:"DEMO-",width:8},properties:[v("Product",40,"Product",false),v("ProductType",4,"Product Type"),v("ProductGroup",9,"Product Group"),v("BaseUnit",3,"Base Unit"),Ne("GrossWeight","Gross Weight",13,3),Ne("NetWeight","Net Weight",13,3),v("WeightUnit",3,"Weight Unit"),v("CreatedByUser",12,"Created By"),ne("CreationDate","Created On"),Mn("IsMarkedForDeletion","Deletion Flag")],navigations:[{name:"to_Description",target:"A_ProductDescription",multiplicity:"*",join:[["Product","Product"]]}]},{set:"A_ProductDescription",keys:["Product","Language"],properties:[v("Product",40,"Product",false),v("Language",2,"Language",false),v("ProductDescription",40,"Description")],navigations:[]}],functionImports:[]},rt=[gi,hi,yi],Wt="MCP_DEMO",Si=[["1000001","2","Nordlicht Maschinenbau GmbH","","Hafenstra\xDFe","12","20457","Hamburg","HH","C","MACH","2024-03-04"],["1000002","2","Weberei Hartmann & S\xF6hne KG","","M\xFChlweg","3","91522","Ansbach","BY","C","TEXT","2024-03-11"],["1000003","2","Fr\xE4nkische Pr\xE4zisionstechnik AG","","Industriestra\xDFe","48","90411","N\xFCrnberg","BY","C","MACH","2024-04-02"],["1000004","2","Altm\xFChl Kunststoffwerk GmbH","","Am Kanal","7","91710","Gunzenhausen","BY","C","CHEM","2024-04-19"],["1000005","2","Seenland Logistik GmbH & Co. KG","","Frankenstra\xDFe","101","91781","Wei\xDFenburg","BY","C","TRAN","2024-05-06"],["1000006","2","Brauerei Hopfenkranz OHG","","Brauhausgasse","2","91154","Roth","BY","C","FOOD","2024-05-21"],["1000007","2","Rothsee Elektronik GmbH","","Gewerbering","15","91126","Schwabach","BY","C","ELEC","2024-06-03"],["1000008","2","Hesselberg Metallbau e.K.","","Bahnhofstra\xDFe","33","91717","Wassertr\xFCdingen","BY","C","MACH","2024-06-17"],["1000009","2","Bavaria Sensorik AG","","Technologiepark","9","91058","Erlangen","BY","C","ELEC","2024-07-01"],["1000010","2","Kastner Werkzeugbau GmbH","","Schmiedeweg","21","90762","F\xFCrth","BY","C","MACH","2024-07-15"],["1000011","2","M\xF6belwerkstatt Lindenhof GmbH","","Lindenallee","5","97070","W\xFCrzburg","BY","C","WOOD","2024-08-05"],["1000012","2","Steinbach Automotive GmbH","","Werkstra\xDFe","60","93055","Regensburg","BY","C","AUTO","2024-08-19"],["1000013","2","Tauber Verpackungen GmbH","","Ringstra\xDFe","18","91541","Rothenburg ob der Tauber","BY","C","PACK","2024-09-02"],["1000014","1","Anna","Berger","Rosenweg","4","96047","Bamberg","BY","C","","2024-09-16"],["1000015","1","Markus","Vogel","Am Stadtpark","11","89073","Ulm","BW","C","","2024-10-07"],["1000016","1","Sabine","Kr\xFCger","Elbchaussee","250","22605","Hamburg","HH","C","","2024-10-21"],["1000017","2","Ruhrstahl Handels GmbH","","Kruppstra\xDFe","77","45143","Essen","NW","S","META","2024-02-05"],["1000018","2","Elbe Chemie AG","","Chemiepark","1","01067","Dresden","SN","S","CHEM","2024-02-12"],["1000019","2","NordPack Verpackungssysteme GmbH","","Hansaring","40","28195","Bremen","HB","S","PACK","2024-02-19"],["1000020","2","Donau Kunststoffe GmbH","","Donaustra\xDFe","88","94032","Passau","BY","B","CHEM","2024-02-26"]],Ti=[["PUMP-100","FERT","PUMPS","ST","42.500","39.800","Kreiselpumpe 100 l/min","Centrifugal pump 100 l/min",1890],["MOTOR-15KW","FERT","DRIVES","ST","118.000","112.500","Drehstrommotor 15 kW","Three-phase motor 15 kW",2450],["SENSOR-T200","HAWA","SENSOR","ST","0.180","0.150","Temperatursensor PT100 -50..200 \xB0C","Temperature sensor PT100 -50..200 \xB0C",64.5],["VALVE-DN50","FERT","VALVES","ST","6.400","6.100","Absperrventil DN50 PN16","Shut-off valve DN50 PN16",312],["HOUSING-AL3","HALB","PARTS","ST","2.900","2.700","Aluminiumgeh\xE4use Gr\xF6\xDFe 3","Aluminium housing size 3",148],["PLC-B200","HAWA","CONTROL","ST","1.100","0.950","Steuerung SPS B200","PLC controller B200",985],["CABLE-5M","HAWA","CABLE","ST","0.600","0.550","Anschlusskabel 5 m geschirmt","Connection cable 5 m shielded",27.9],["FILTER-F40","HAWA","FILTER","ST","0.900","0.800","Filtereinsatz F40","Filter cartridge F40",45],["SEAL-KIT-3","HAWA","PARTS","ST","0.250","0.200","Dichtungssatz Baugr\xF6\xDFe 3","Seal kit size 3",38],["BRACKET-XL","HALB","PARTS","ST","3.300","3.100","Wandhalterung XL verzinkt","Wall bracket XL galvanised",72]],Ai=[["4500001","1000001","PO-2026-0117","2026-01-08","2026-01-29","C","C",[["PUMP-100",2],["SEAL-KIT-3",4]]],["4500002","1000003","BEST-88123","2026-01-12","2026-02-05","C","C",[["MOTOR-15KW",1],["CABLE-5M",6],["PLC-B200",1]]],["4500003","1000007","RE-2026-014","2026-01-19","2026-02-10","C","C",[["SENSOR-T200",25],["CABLE-5M",25]]],["4500004","1000004","AK-4471","2026-02-02","2026-02-27","C","C",[["VALVE-DN50",8],["SEAL-KIT-3",8]]],["4500005","1000009","BS-2026-0301","2026-02-16","2026-03-13","C","B",[["SENSOR-T200",120],["HOUSING-AL3",40],["BRACKET-XL",40]]],["4500006","1000012","SA-77201","2026-03-03","2026-03-31","B","B",[["MOTOR-15KW",3],["PLC-B200",3],["CABLE-5M",12]]],["4500007","1000010","KW-2026-19","2026-03-10","2026-04-07","B","A",[["PUMP-100",1],["FILTER-F40",10]]],["4500008","1000005","SL-0209","2026-03-24","2026-04-14","B","A",[["BRACKET-XL",30],["HOUSING-AL3",12]]],["4500009","1000002","WH-2026-04","2026-04-06","2026-05-04","B","A",[["MOTOR-15KW",2],["SENSOR-T200",10]]],["4500010","1000014","","2026-04-14","2026-04-28","C","C",[["FILTER-F40",2],["SEAL-KIT-3",1]]],["4500011","1000011","ML-3350","2026-05-05","2026-06-02","A","A",[["VALVE-DN50",4],["PUMP-100",1],["CABLE-5M",4]]],["4500012","1000013","TV-2026-0888","2026-05-18","2026-06-15","A","A",[["PLC-B200",2],["SENSOR-T200",40],["CABLE-5M",40],["BRACKET-XL",8]]],["4500013","1000015","","2026-06-01","2026-06-22","A","A",[["FILTER-F40",5]]],["4500014","1000006","HK-2026-11","2026-06-15","2026-07-13","A","A",[["PUMP-100",3],["VALVE-DN50",6],["SEAL-KIT-3",6]]],["4500015","1000008","HM-0521","2026-07-01","2026-07-29","A","A",[["MOTOR-15KW",1],["HOUSING-AL3",20],["PLC-B200",1]]]];function qt(t,e=3){return t.toFixed(e)}function Gt(){let t=[],e=[];for(let[c,l,d,u,m,p,f,g,h,S,T,y]of Si){let b=l==="1",D=b?`${d} ${u}`:d;t.push({BusinessPartner:c,Customer:S==="C"||S==="B"?c:"",Supplier:S==="S"||S==="B"?c:"",BusinessPartnerCategory:l,BusinessPartnerFullName:D,BusinessPartnerName:D,OrganizationBPName1:b?"":d,FirstName:b?d:"",LastName:b?u:"",BusinessPartnerGrouping:b?"BP02":"BP01",SearchTerm1:(b?u:d.split(" ")[0]).toUpperCase().slice(0,20),Industry:T,CreatedByUser:Wt,CreationDate:te(y),IsMarkedForArchiving:false}),e.push({BusinessPartner:c,AddressID:String(2e4+t.length),ValidityStartDate:te(y),ValidityEndDate:te("9999-12-31"),StreetName:m,HouseNumber:p,PostalCode:f,CityName:g,Country:"DE",Region:h,Language:"DE"});}let n=[],r=[],s=new Map,i=new Map;Ti.forEach(([c,l,d,u,m,p,f,g,h],S)=>{s.set(c,h),i.set(c,g),n.push({Product:c,ProductType:l,ProductGroup:d,BaseUnit:u,GrossWeight:m,NetWeight:p,WeightUnit:"KG",CreatedByUser:Wt,CreationDate:te(`2023-11-${String(6+S).padStart(2,"0")}`),IsMarkedForDeletion:false}),r.push({Product:c,Language:"DE",ProductDescription:f}),r.push({Product:c,Language:"EN",ProductDescription:g});});let o=[],a=[];for(let[c,l,d,u,m,p,f,g]of Ai){let h=0;g.forEach(([S,T],y)=>{let b=T*(s.get(S)??0);h+=b,a.push({SalesOrder:c,SalesOrderItem:String((y+1)*10),Material:S,SalesOrderItemText:i.get(S)??S,SalesOrderItemCategory:"TAN",RequestedQuantity:qt(T),RequestedQuantityUnit:"ST",NetAmount:qt(b),TransactionCurrency:"EUR",ProductionPlant:"1010"});}),o.push({SalesOrder:c,SalesOrderType:"OR",SalesOrganization:"1010",DistributionChannel:"10",OrganizationDivision:"00",SoldToParty:l,PurchaseOrderByCustomer:d,CreatedByUser:Wt,CreationDate:te(u),LastChangeDateTime:te(`${u}T09:30:00Z`),RequestedDeliveryDate:te(m),TotalNetAmount:qt(h),TransactionCurrency:"EUR",OverallSDProcessStatus:p,OverallDeliveryStatus:f});}return {A_BusinessPartner:t,A_BusinessPartnerAddress:e,A_SalesOrder:o,A_SalesOrderItem:a,A_Product:n,A_ProductDescription:r}}function bi(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function vi(t,e){return `assoc_${t.set}_${e}`}function Un(t){let e=t.namespace,n=[],r=[],s=[];for(let a of t.entityTypes){let c=a.properties.map(d=>{let u=[`Name="${d.name}"`,`Type="${d.type}"`,`Nullable="${d.nullable===false||a.keys.includes(d.name)?"false":"true"}"`];return d.maxLength!==void 0&&u.push(`MaxLength="${d.maxLength}"`),d.precision!==void 0&&u.push(`Precision="${d.precision}"`),d.scale!==void 0&&u.push(`Scale="${d.scale}"`),d.label&&u.push(`sap:label="${bi(d.label)}"`),a.keys.includes(d.name)&&u.push('sap:creatable="true" sap:updatable="false"'),` <Property ${u.join(" ")}/>`}).join(`
|
|
5
|
+
`),l=a.navigations.map(d=>{let u=vi(a,d.name),m=`FromRole_${u}`,p=`ToRole_${u}`;return r.push(` <Association Name="${u}" sap:content-version="1">
|
|
6
6
|
<End Type="${e}.${a.set}Type" Multiplicity="1" Role="${m}"/>
|
|
7
7
|
<End Type="${e}.${d.target}Type" Multiplicity="${d.multiplicity}" Role="${p}"/>
|
|
8
8
|
</Association>`),s.push(` <AssociationSet Name="${u}" Association="${e}.${u}" sap:creatable="false" sap:updatable="false" sap:deletable="false" sap:content-version="1">
|
|
@@ -38,7 +38,7 @@ ${o}`:""}
|
|
|
38
38
|
</Schema>
|
|
39
39
|
</edmx:DataServices>
|
|
40
40
|
</edmx:Edmx>
|
|
41
|
-
`}var I=class extends Error{constructor(e){super(e),this.name="ODataSyntaxError";}},$n=new Set(["and","or","not","eq","ne","gt","ge","lt","le"]);function vi(t){let e=[],n=0;for(;n<t.length;){let r=t[n];if(/\s/.test(r)){n++;continue}if(r==="("){e.push({kind:"lparen"}),n++;continue}if(r===")"){e.push({kind:"rparen"}),n++;continue}if(r===","){e.push({kind:"comma"}),n++;continue}if(r==="'"){let{value:s,end:i}=Ln(t,n);e.push({kind:"string",value:s}),n=i;continue}if(/[0-9]/.test(r)||r==="-"&&/[0-9]/.test(t[n+1]??"")){let s=/^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?[mMdDfFlL]?/.exec(t.slice(n));if(!s)throw new I(`Ung\xFCltige Zahl an Position ${n}.`);e.push({kind:"number",value:Number(s[0].replace(/[mMdDfFlL]$/,""))}),n+=s[0].length;continue}if(/[A-Za-z_]/.test(r)){let i=/^[A-Za-z_][A-Za-z0-9_./]*/.exec(t.slice(n))[0];if(n+=i.length,t[n]==="'"){let{value:o,end:a}=Ln(t,n);n=a;let c=i.toLowerCase();if(c==="datetime"||c==="datetimeoffset"){let l=Fn(o);if(Number.isNaN(l))throw new I(`Ung\xFCltiges datetime-Literal '${o}'.`);e.push({kind:"datetime",value:l});}else if(c==="guid"||c==="x"||c==="binary"||c==="time")e.push({kind:"string",value:o});else throw new I(`Unbekanntes Literal-Pr\xE4fix '${i}'.`);continue}e.push({kind:"ident",value:i});continue}throw new I(`Unerwartetes Zeichen '${r}' an Position ${n}.`)}return e}function Ln(t,e){let n=e+1,r="";for(;n<t.length;){let s=t[n];if(s==="'"){if(t[n+1]==="'"){r+="'",n+=2;continue}return {value:r,end:n+1}}r+=s,n++;}throw new I("Zeichenkette ohne schlie\xDFendes Apostroph.")}function Fn(t){let e=/[zZ]|[+-]\d{2}:\d{2}$/.test(t)?t:`${t}Z`;return Date.parse(e)}var Ei=new Set(["substringof","startswith","endswith","tolower","toupper"]),zt=class{constructor(e){this.tokens=e;}pos=0;parse(){if(this.tokens.length===0)throw new I("Leerer $filter-Ausdruck.");let e=this.parseOr();if(this.pos<this.tokens.length)throw new I(`Unerwartetes Token nach dem Ausdruck: ${Bn(this.peek())}.`);return e}peek(){return this.tokens[this.pos]}isKeyword(e){let n=this.peek();return n?.kind==="ident"&&n.value.toLowerCase()===e}parseOr(){let e=this.parseAnd();for(;this.isKeyword("or");)this.pos++,e={type:"or",left:e,right:this.parseAnd()};return e}parseAnd(){let e=this.parseNot();for(;this.isKeyword("and");)this.pos++,e={type:"and",left:e,right:this.parseNot()};return e}parseNot(){return this.isKeyword("not")?(this.pos++,{type:"not",operand:this.parseNot()}):this.parseComparison()}parseComparison(){let e=this.parsePrimary(),n=this.peek();if(n?.kind==="ident"){let r=n.value.toLowerCase();if(r==="eq"||r==="ne"||r==="gt"||r==="ge"||r==="lt"||r==="le"){this.pos++;let s=this.parsePrimary();return {type:"compare",op:r,left:e,right:s}}if(!$n.has(r))throw new I(`Unbekannter Operator '${n.value}'.`)}return e}parsePrimary(){let e=this.peek();if(!e)throw new I("Ausdruck endet unerwartet.");if(e.kind==="lparen"){this.pos++;let n=this.parseOr();if(this.peek()?.kind!=="rparen")throw new I("Schlie\xDFende Klammer fehlt.");return this.pos++,n}if(e.kind==="string")return this.pos++,{type:"literal",value:e.value};if(e.kind==="number")return this.pos++,{type:"literal",value:e.value};if(e.kind==="datetime")return this.pos++,{type:"literal",value:e.value,datetime:true};if(e.kind==="ident"){let n=e.value.toLowerCase();if(n==="true"||n==="false")return this.pos++,{type:"literal",value:n==="true"};if(n==="null")return this.pos++,{type:"literal",value:null};if($n.has(n))throw new I(`Operand erwartet, aber '${e.value}' gefunden.`);if(this.pos++,this.peek()?.kind==="lparen"){if(!Ei.has(n))throw new I(`Unbekannte Funktion '${e.value}'.`);this.pos++;let r=[];if(this.peek()?.kind!=="rparen")for(r.push(this.parseOr());this.peek()?.kind==="comma";)this.pos++,r.push(this.parseOr());if(this.peek()?.kind!=="rparen")throw new I(`Schlie\xDFende Klammer nach ${e.value}(...) fehlt.`);return this.pos++,{type:"call",fn:n,args:r}}return {type:"field",path:e.value}}throw new I(`Unerwartetes Token ${Bn(e)}.`)}};function Bn(t){if(!t)return "(Ende)";switch(t.kind){case "lparen":return "'('";case "rparen":return "')'";case "comma":return "','";default:return `'${String(t.value)}'`}}var Ci=/^\/Date\((-?\d+)(?:[+-]\d{4})?\)\/$/;function Pi(t,e){let n=t;for(let r of e.split("/")){if(n===null||typeof n!="object")return null;n=n[r];}return n===void 0?null:n}function j(t,e){switch(t.type){case "and":return ge(j(t.left,e))&&ge(j(t.right,e));case "or":return ge(j(t.left,e))||ge(j(t.right,e));case "not":return !ge(j(t.operand,e));case "literal":return t.datetime?{datetime:t.value}:t.value;case "field":return Pi(e,t.path);case "call":{let n=t.args.map(s=>j(s,e)),r=s=>s==null?"":String(s);switch(t.fn){case "substringof":return r(n[1]).includes(r(n[0]));case "startswith":return r(n[0]).startsWith(r(n[1]));case "endswith":return r(n[0]).endsWith(r(n[1]));case "tolower":return r(n[0]).toLowerCase();case "toupper":return r(n[0]).toUpperCase();default:throw new I(`Unbekannte Funktion '${t.fn}'.`)}}case "compare":{let[n,r]=Ri(j(t.left,e),j(t.right,e));switch(t.op){case "eq":return n===r;case "ne":return n!==r;case "gt":return n!==null&&r!==null&&n>r;case "ge":return n!==null&&r!==null&&n>=r;case "lt":return n!==null&&r!==null&&n<r;case "le":return n!==null&&r!==null&&n<=r}}}}function ge(t){return t===true}function Ri(t,e){let n=i=>typeof i=="object"&&i!==null&&"datetime"in i;if(n(t)||n(e)){let i=o=>{if(n(o))return o.datetime;if(typeof o=="number")return o;if(typeof o=="string"){let a=Ci.exec(o);if(a)return Number(a[1]);let c=Fn(o);return Number.isNaN(c)?null:c}return null};return [i(t),i(e)]}let r=i=>{if(typeof i=="number")return i;if(typeof i=="string"&&i.trim()!==""&&!Number.isNaN(Number(i)))return Number(i)};if((typeof t=="number"||typeof e=="number")&&!(t===null||e===null)){let i=r(t),o=r(e);if(i!==void 0&&o!==void 0)return [i,o]}let s=i=>i===void 0?null:typeof i=="object"&&i!==null?String(i):i;return [s(t),s(e)]}function Hn(t){let e=new zt(vi(t)).parse();return n=>ge(j(e,n))}var he=class extends Error{constructor(e){super(e),this.name="ODataUrlError";}};function st(t){let e=t.trim();if(e.startsWith("'"))return _i(e);let n=/^(guid|datetime|datetimeoffset|time|X|binary)'(.*)'$/is.exec(e);return n?n[2].replace(/''/g,"'"):/^-?\d+(\.\d+)?[mMdDfFlL]?$/.test(e)?Number(e.replace(/[mMdDfFlL]$/,"")):e==="true"?true:e==="false"?false:e}function _i(t){if(!t.endsWith("'")||t.length<2)throw new he(`Zeichenkette ohne schlie\xDFendes Apostroph: ${t}`);return t.slice(1,-1).replace(/''/g,"'")}function ki(t){let e=Yt(t);return Ii(e,",").map(r=>{let s=Di(r,"=");return s===-1?{value:st(r)}:{name:r.slice(0,s).trim(),value:st(r.slice(s+1))}})}function Xt(t){let e=t.replace(/^\/+|\/+$/g,""),n=e.indexOf("("),r=e.indexOf("/"),s,i,o;if(n!==-1&&(r===-1||n<r)){s=e.slice(0,n);let l=Oi(e,n);i=ki(e.slice(n+1,l)),o=e.slice(l+1);}else r!==-1?(s=e.slice(0,r),o=e.slice(r)):(s=e,o="");let a=o.split("/").map(l=>Yt(l)).filter(l=>l.length>0),c={entitySet:Yt(s),segments:a};return i&&(c.key=i),c}function Oi(t,e){let n=false;for(let r=e+1;r<t.length;r++){let s=t[r];if(s==="'"){n=!n;continue}if(!n&&s===")")return r}throw new he(`Schlie\xDFende Klammer fehlt in: ${t}`)}function Ii(t,e){let n=[],r="",s=false;for(let i=0;i<t.length;i++){let o=t[i];if(o==="'"){s=!s,r+=o;continue}if(!s&&o===e){n.push(r),r="";continue}r+=o;}return n.push(r),n.filter(i=>i.trim().length>0)}function Di(t,e){let n=false;for(let r=0;r<t.length;r++)if(t[r]==="'")n=!n;else if(!n&&t[r]===e)return r;return -1}function Yt(t){try{return decodeURIComponent(t)}catch{return t}}var it="DEMO",Zt="demo",en="100",xe="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2",Ni="/sap/opu/odata4/",A=class extends Error{constructor(n,r,s,i={}){super(s);this.status=n;this.code=r;this.headers=i;}},ot=class{constructor(e={}){this.options=e;for(let n of rt)for(let r of n.entityTypes)this.types.set(r.set,{type:r,service:n});}data=Gt();versions=new Map;csrfToken=randomBytes(16).toString("base64url");cookie=`SAP_SESSIONID_DEMO_100=${randomBytes(12).toString("base64url")}`;types=new Map;nextNumber=new Map;reset(){this.data=Gt(),this.versions.clear(),this.nextNumber.clear();}rows(e){return this.data[e]??[]}handle(e){let r={base:this.options.baseUrl??`http://${e.headers.host??"127.0.0.1"}`,inBatch:false};try{return this.dispatch(e,r)}catch(s){return this.errorResponse(s)}}dispatch(e,n){let r=e.method.toUpperCase(),s=new URL(e.url,n.base),i=s.pathname.replace(/\/+$/,"")||"/",o=s.searchParams;n.inBatch||this.checkAuth(e.headers,o);let c=(e.headers["x-csrf-token"]??"").toLowerCase()==="fetch"?{"x-csrf-token":this.csrfToken,"set-cookie":`${this.cookie}; path=/`}:{},l=r==="HEAD",d=l?"GET":r,u=d==="POST"&&(e.headers["x-http-method"]??"").toUpperCase()==="MERGE"?"MERGE":d;if(u!=="GET"&&!n.inBatch&&e.headers["x-csrf-token"]!==this.csrfToken)throw new A(403,"HTTP/403","CSRF token validation failed",{"x-csrf-token":"Required"});let p;if(i===xe||i.startsWith(`${xe}/`))p=this.handleCatalog(u,i.slice(xe.length),o,n);else {if(i.startsWith(Ni))throw new A(404,"HTTP/404",`Resource not found for segment '${i}'`);{let f=rt.find(g=>i===g.path||i.startsWith(`${g.path}/`));if(!f)throw new A(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${i}'`);p=this.handleService(f,u,i.slice(f.path.length),o,e,n);}}return p.headers={...p.headers,...c},l&&(p.body=""),p}checkAuth(e,n){let r=e.authorization??"",s=`Basic ${Buffer.from(`${this.options.username??it}:${this.options.password??Zt}`).toString("base64")}`,i=a=>new A(401,"HTTP/401",a,{"www-authenticate":'Basic realm="SAP NetWeaver Application Server [DEMO/100]"'});if(r!==s)throw i("Anmeldung fehlgeschlagen");let o=n.get("sap-client");if(o!==(this.options.sapClient??en))throw i(o?`Mandant ${o} existiert nicht`:"Anmeldung fehlgeschlagen")}handleCatalog(e,n,r,s){if(e!=="GET")throw new A(405,"HTTP/405","Method not allowed on catalog");let i=n.replace(/^\/+/,"");if(i==="")return this.json(200,{d:{EntitySets:["ServiceCollection"]}});if(i==="$metadata")return this.xml(200,'<?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx"><edmx:DataServices m:DataServiceVersion="2.0" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"><Schema Namespace="CATALOGSERVICE" xmlns="http://schemas.microsoft.com/ado/2008/09/edm"><EntityType Name="Service"><Key><PropertyRef Name="ID"/></Key><Property Name="ID" Type="Edm.String" Nullable="false"/><Property Name="TechnicalServiceName" Type="Edm.String"/><Property Name="Title" Type="Edm.String"/><Property Name="Description" Type="Edm.String"/><Property Name="ServiceUrl" Type="Edm.String"/><Property Name="TechnicalServiceVersion" Type="Edm.String"/></EntityType><EntityContainer Name="CATALOGSERVICE_Entities" m:IsDefaultEntityContainer="true"><EntitySet Name="ServiceCollection" EntityType="CATALOGSERVICE.Service"/></EntityContainer></Schema></edmx:DataServices></edmx:Edmx>');let o=Xt(i);if(o.entitySet!=="ServiceCollection")throw new A(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${i}'`);let a=[...rt].map(u=>({__metadata:{id:`${s.base}${xe}/ServiceCollection('${u.technicalName}_0001')`,uri:`${s.base}${xe}/ServiceCollection('${u.technicalName}_0001')`,type:"CATALOGSERVICE.Service"},ID:`${u.technicalName}_0001`,TechnicalServiceName:u.technicalName,Title:u.title,Description:u.description,ServiceUrl:`${s.base}${u.path}`,TechnicalServiceVersion:"0001",MetadataUrl:`${s.base}${u.path}/$metadata`}));if(o.key){let u=String(o.key[0]?.value??""),m=a.find(p=>p.ID===u);if(!m)throw new A(404,"/IWFND/CM_MGW/002",`Service '${u}' not found`);return this.json(200,{d:m})}let c=Wn(r),{rows:l,count:d}=Jt(a,c);return this.json(200,{d:{results:l,...c.inlinecount==="allpages"?{__count:String(d)}:{}}})}handleService(e,n,r,s,i,o){let a=r.replace(/^\/+/,"");if(a===""){if(n!=="GET")throw new A(405,"HTTP/405","Method not allowed");return this.json(200,{d:{EntitySets:e.entityTypes.map(l=>l.set)}})}if(a==="$metadata")return this.xml(200,Un(e));if(a==="$batch"){if(n!=="POST")throw new A(405,"HTTP/405","Method not allowed on $batch");return this.handleBatch(e,i,o)}let c=e.functionImports.find(l=>l.name===a);if(c){if(n!==c.httpMethod&&!(n==="MERGE"&&c.httpMethod==="POST"))throw new A(405,"HTTP/405",`Function import ${c.name} requires ${c.httpMethod}`);return this.handleFunctionImport(e,c.name,s,o)}return this.handleEntity(e,n,a,s,i,o)}handleFunctionImport(e,n,r,s){let i=e.functionImports.find(a=>a.name===n),o={};for(let a of i.parameters){let c=r.get(a.name);if(c===null){if(!a.nullable)throw new A(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Parameter '${a.name}' is missing`);continue}o[a.name]=st(c);}if(n==="ReleaseSalesOrder"){let a=String(o.SalesOrder??""),c=this.rows("A_SalesOrder").find(l=>l.SalesOrder===a);if(!c)throw new A(404,"SD/001",`Sales order ${a} does not exist`);if(c.OverallSDProcessStatus==="C")throw new A(400,"SD/002",`Sales order ${a} is already completed and cannot be released`);return c.OverallSDProcessStatus="B",c.LastChangeDateTime=`/Date(${Date.now()})/`,this.bump("A_SalesOrder",c),this.json(200,{d:this.present(e,this.typeOf("A_SalesOrder"),c,s,{})})}if(n==="GetOpenSalesOrderCount"){let a=o.SoldToParty===void 0?void 0:String(o.SoldToParty),c=this.rows("A_SalesOrder").filter(l=>l.OverallSDProcessStatus!=="C"&&(a===void 0||l.SoldToParty===a)).length;return this.json(200,{d:{GetOpenSalesOrderCount:c}})}throw new A(501,"HTTP/501",`Function import ${n} not implemented`)}handleEntity(e,n,r,s,i,o){let a=i.body,c=i.headers["if-match"],l;try{l=Xt(r);}catch(h){throw h instanceof he?new A(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR",h.message):h}let d=e.entityTypes.find(h=>h.set===l.entitySet);if(!d)throw new A(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${l.entitySet}'`);let u=Wn(s),m=this.rows(d.set),p;if(l.key&&(p=this.find(d,l.key),!p))throw new A(404,"/IWBEP/CM_MGW_RT/020",`Resource not found for the segment '${d.set}'`);let f=[...l.segments],g=false;if(f[f.length-1]==="$count"&&(g=true,f.pop()),f.length>0){if(!p)throw new A(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR","Navigation requires a key");if(f.length>1)throw new A(501,"HTTP/501","Only one navigation level is supported");let h=d.navigations.find(y=>y.name===f[0]);if(!h)throw new A(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${f[0]}'`);let S=this.typeOf(h.target),T=this.related(p,h);if(h.multiplicity==="1"){if(p=T[0],!p)throw new A(404,"/IWBEP/CM_MGW_RT/020",`No ${h.target} for this ${d.set}`);d=S,m=[];}else m=T,p=void 0,d=S;}switch(n){case "GET":{if(p){let T=this.json(200,{d:this.present(e,d,p,o,u)});return T.headers.etag=this.etag(d,p),T}if(g){let T=Jt(m,{...u,top:void 0,skip:void 0,raw:u.raw});return {status:200,headers:{"content-type":"text/plain"},body:String(T.count)}}let{rows:h,count:S}=Jt(m,u);return this.json(200,{d:{results:h.map(T=>this.present(e,d,T,o,u)),...u.inlinecount==="allpages"?{__count:String(S)}:{}}})}case "POST":{if(p||l.key)throw new A(405,"HTTP/405","POST on a single entity is not allowed (use MERGE/PATCH)");if(d.creatable===false)throw new A(405,"HTTP/405",`${d.set} is not creatable`);let h=this.parseBody(a),S=this.create(d,h,o),T=this.json(201,{d:this.present(e,d,S,o,{expand:this.deepExpands(d,h)})});return T.headers.location=this.uri(o,e,d,S),T.headers.etag=this.etag(d,S),T}case "MERGE":case "PATCH":case "PUT":{if(!p)throw new A(405,"HTTP/405",`${n} requires a single entity`);if(d.updatable===false)throw new A(405,"HTTP/405",`${d.set} is not updatable`);this.checkIfMatch(d,p,c);let h=this.parseBody(a);return this.update(d,p,h,n==="PUT"),{status:204,headers:{etag:this.etag(d,p),dataserviceversion:"2.0"},body:""}}case "DELETE":{if(!p)throw new A(405,"HTTP/405","DELETE requires a single entity");if(d.deletable===false)throw new A(405,"HTTP/405",`${d.set} is not deletable`);return this.checkIfMatch(d,p,c),this.remove(d,p),{status:204,headers:{dataserviceversion:"2.0"},body:""}}default:throw new A(405,"HTTP/405",`Method ${n} not allowed`)}}parseBody(e){if(!e||e.trim()==="")throw new A(400,"/IWCOR/CX_DS_BODY_MISSING","Request body is empty");let n;try{n=JSON.parse(e);}catch{throw new A(400,"/IWCOR/CX_DS_JSON_SYNTAX_ERROR","Request body is not valid JSON")}if(n===null||typeof n!="object"||Array.isArray(n))throw new A(400,"/IWCOR/CX_DS_JSON_SYNTAX_ERROR","Request body must be a JSON object");let r=n;return "d"in r&&typeof r.d=="object"&&r.d!==null&&Object.keys(r).length===1?r.d:r}create(e,n,r,s={}){let i={},o=[];for(let[a,c]of Object.entries(n)){if(a==="__metadata")continue;let l=e.navigations.find(u=>u.name===a);if(l){if(l.multiplicity!=="*")continue;let u=Array.isArray(c)?c:c&&typeof c=="object"&&Array.isArray(c.results)?c.results:[];o.push({nav:l,children:u});continue}let d=e.properties.find(u=>u.name===a);if(!d)throw new A(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Property '${a}' is invalid`);i[a]=this.coerce(d.name,d.type,c);}Object.assign(i,s);for(let a of e.keys)(i[a]===void 0||i[a]==="")&&(i[a]=this.nextKey(e,a,i));if(this.find(e,e.keys.map(a=>({name:a,value:i[a]}))))throw new A(400,"/IWBEP/CX_MGW_BUSI_EXCEPTION",`${e.set} with this key already exists`);for(let a of e.properties)i[a.name]===void 0&&(i[a.name]=this.emptyValue(a.type));this.defaults(e,i),this.rows(e.set).push(i),this.bump(e.set,i);for(let{nav:a,children:c}of o){let l=this.typeOf(a.target),d={};for(let[u,m]of a.join)d[m]=i[u];for(let u of c)this.create(l,u,r,d);}return this.recalculate(e,i),i}update(e,n,r,s){let i={};for(let[o,a]of Object.entries(r)){if(o==="__metadata"||e.navigations.some(l=>l.name===o))continue;let c=e.properties.find(l=>l.name===o);if(!c)throw new A(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Property '${o}' is invalid`);if(e.keys.includes(o)&&this.coerce(o,c.type,a)!==n[o])throw new A(400,"/IWBEP/CX_MGW_BUSI_EXCEPTION",`Key property '${o}' cannot be changed`);i[o]=this.coerce(c.name,c.type,a);}if(s)for(let o of e.properties)!e.keys.includes(o.name)&&!(o.name in i)&&(n[o.name]=this.emptyValue(o.type));if(Object.assign(n,i),(e.set==="A_SalesOrder"||e.set==="A_SalesOrderItem")&&(n.LastChangeDateTime=`/Date(${Date.now()})/`,e.set==="A_SalesOrderItem")){let o=this.rows("A_SalesOrder").find(a=>a.SalesOrder===n.SalesOrder);o&&this.recalculate(this.typeOf("A_SalesOrder"),o);}this.bump(e.set,n);}remove(e,n){let r=this.rows(e.set),s=r.indexOf(n);s!==-1&&r.splice(s,1),this.versions.delete(this.versionKey(e.set,n));for(let i of e.navigations){if(i.multiplicity!=="*")continue;let o=this.typeOf(i.target);for(let a of this.related(n,i))this.remove(o,a);}}checkIfMatch(e,n,r){if(r===void 0||r.trim()==="*")return;let s=this.etag(e,n);if(!r.split(",").map(o=>o.trim()).map(o=>o.startsWith("W/")?o:`W/${o}`).includes(s))throw new A(412,"/IWBEP/CM_MGW_RT/021","The entity has been changed by another user; reload and retry (ETag mismatch)")}handleBatch(e,n,r){let s=n.headers["content-type"]??"",i=/boundary=("?)([^";]+)\1/i.exec(s)?.[2];if(!i||!n.body)throw new A(400,"/IWCOR/CX_DS_BATCH","multipart/mixed body with boundary expected");let o=`batchresponse_${randomUUID()}`,a=[];for(let c of qn(n.body,i)){let l=/content-type:\s*multipart\/mixed;\s*boundary=("?)([^";\r\n]+)\1/i.exec(c);if(l){let d=l[2],u=c.indexOf(`\r
|
|
41
|
+
`}var I=class extends Error{constructor(e){super(e),this.name="ODataSyntaxError";}},$n=new Set(["and","or","not","eq","ne","gt","ge","lt","le"]);function wi(t){let e=[],n=0;for(;n<t.length;){let r=t[n];if(/\s/.test(r)){n++;continue}if(r==="("){e.push({kind:"lparen"}),n++;continue}if(r===")"){e.push({kind:"rparen"}),n++;continue}if(r===","){e.push({kind:"comma"}),n++;continue}if(r==="'"){let{value:s,end:i}=Ln(t,n);e.push({kind:"string",value:s}),n=i;continue}if(/[0-9]/.test(r)||r==="-"&&/[0-9]/.test(t[n+1]??"")){let s=/^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?[mMdDfFlL]?/.exec(t.slice(n));if(!s)throw new I(`Ung\xFCltige Zahl an Position ${n}.`);e.push({kind:"number",value:Number(s[0].replace(/[mMdDfFlL]$/,""))}),n+=s[0].length;continue}if(/[A-Za-z_]/.test(r)){let i=/^[A-Za-z_][A-Za-z0-9_./]*/.exec(t.slice(n))[0];if(n+=i.length,t[n]==="'"){let{value:o,end:a}=Ln(t,n);n=a;let c=i.toLowerCase();if(c==="datetime"||c==="datetimeoffset"){let l=Fn(o);if(Number.isNaN(l))throw new I(`Ung\xFCltiges datetime-Literal '${o}'.`);e.push({kind:"datetime",value:l});}else if(c==="guid"||c==="x"||c==="binary"||c==="time")e.push({kind:"string",value:o});else throw new I(`Unbekanntes Literal-Pr\xE4fix '${i}'.`);continue}e.push({kind:"ident",value:i});continue}throw new I(`Unerwartetes Zeichen '${r}' an Position ${n}.`)}return e}function Ln(t,e){let n=e+1,r="";for(;n<t.length;){let s=t[n];if(s==="'"){if(t[n+1]==="'"){r+="'",n+=2;continue}return {value:r,end:n+1}}r+=s,n++;}throw new I("Zeichenkette ohne schlie\xDFendes Apostroph.")}function Fn(t){let e=/[zZ]|[+-]\d{2}:\d{2}$/.test(t)?t:`${t}Z`;return Date.parse(e)}var Ei=new Set(["substringof","startswith","endswith","tolower","toupper"]),zt=class{constructor(e){this.tokens=e;}pos=0;parse(){if(this.tokens.length===0)throw new I("Leerer $filter-Ausdruck.");let e=this.parseOr();if(this.pos<this.tokens.length)throw new I(`Unerwartetes Token nach dem Ausdruck: ${Bn(this.peek())}.`);return e}peek(){return this.tokens[this.pos]}isKeyword(e){let n=this.peek();return n?.kind==="ident"&&n.value.toLowerCase()===e}parseOr(){let e=this.parseAnd();for(;this.isKeyword("or");)this.pos++,e={type:"or",left:e,right:this.parseAnd()};return e}parseAnd(){let e=this.parseNot();for(;this.isKeyword("and");)this.pos++,e={type:"and",left:e,right:this.parseNot()};return e}parseNot(){return this.isKeyword("not")?(this.pos++,{type:"not",operand:this.parseNot()}):this.parseComparison()}parseComparison(){let e=this.parsePrimary(),n=this.peek();if(n?.kind==="ident"){let r=n.value.toLowerCase();if(r==="eq"||r==="ne"||r==="gt"||r==="ge"||r==="lt"||r==="le"){this.pos++;let s=this.parsePrimary();return {type:"compare",op:r,left:e,right:s}}if(!$n.has(r))throw new I(`Unbekannter Operator '${n.value}'.`)}return e}parsePrimary(){let e=this.peek();if(!e)throw new I("Ausdruck endet unerwartet.");if(e.kind==="lparen"){this.pos++;let n=this.parseOr();if(this.peek()?.kind!=="rparen")throw new I("Schlie\xDFende Klammer fehlt.");return this.pos++,n}if(e.kind==="string")return this.pos++,{type:"literal",value:e.value};if(e.kind==="number")return this.pos++,{type:"literal",value:e.value};if(e.kind==="datetime")return this.pos++,{type:"literal",value:e.value,datetime:true};if(e.kind==="ident"){let n=e.value.toLowerCase();if(n==="true"||n==="false")return this.pos++,{type:"literal",value:n==="true"};if(n==="null")return this.pos++,{type:"literal",value:null};if($n.has(n))throw new I(`Operand erwartet, aber '${e.value}' gefunden.`);if(this.pos++,this.peek()?.kind==="lparen"){if(!Ei.has(n))throw new I(`Unbekannte Funktion '${e.value}'.`);this.pos++;let r=[];if(this.peek()?.kind!=="rparen")for(r.push(this.parseOr());this.peek()?.kind==="comma";)this.pos++,r.push(this.parseOr());if(this.peek()?.kind!=="rparen")throw new I(`Schlie\xDFende Klammer nach ${e.value}(...) fehlt.`);return this.pos++,{type:"call",fn:n,args:r}}return {type:"field",path:e.value}}throw new I(`Unerwartetes Token ${Bn(e)}.`)}};function Bn(t){if(!t)return "(Ende)";switch(t.kind){case "lparen":return "'('";case "rparen":return "')'";case "comma":return "','";default:return `'${String(t.value)}'`}}var Ci=/^\/Date\((-?\d+)(?:[+-]\d{4})?\)\/$/;function Pi(t,e){let n=t;for(let r of e.split("/")){if(n===null||typeof n!="object")return null;n=n[r];}return n===void 0?null:n}function j(t,e){switch(t.type){case "and":return ge(j(t.left,e))&&ge(j(t.right,e));case "or":return ge(j(t.left,e))||ge(j(t.right,e));case "not":return !ge(j(t.operand,e));case "literal":return t.datetime?{datetime:t.value}:t.value;case "field":return Pi(e,t.path);case "call":{let n=t.args.map(s=>j(s,e)),r=s=>s==null?"":String(s);switch(t.fn){case "substringof":return r(n[1]).includes(r(n[0]));case "startswith":return r(n[0]).startsWith(r(n[1]));case "endswith":return r(n[0]).endsWith(r(n[1]));case "tolower":return r(n[0]).toLowerCase();case "toupper":return r(n[0]).toUpperCase();default:throw new I(`Unbekannte Funktion '${t.fn}'.`)}}case "compare":{let[n,r]=Ri(j(t.left,e),j(t.right,e));switch(t.op){case "eq":return n===r;case "ne":return n!==r;case "gt":return n!==null&&r!==null&&n>r;case "ge":return n!==null&&r!==null&&n>=r;case "lt":return n!==null&&r!==null&&n<r;case "le":return n!==null&&r!==null&&n<=r}}}}function ge(t){return t===true}function Ri(t,e){let n=i=>typeof i=="object"&&i!==null&&"datetime"in i;if(n(t)||n(e)){let i=o=>{if(n(o))return o.datetime;if(typeof o=="number")return o;if(typeof o=="string"){let a=Ci.exec(o);if(a)return Number(a[1]);let c=Fn(o);return Number.isNaN(c)?null:c}return null};return [i(t),i(e)]}let r=i=>{if(typeof i=="number")return i;if(typeof i=="string"&&i.trim()!==""&&!Number.isNaN(Number(i)))return Number(i)};if((typeof t=="number"||typeof e=="number")&&!(t===null||e===null)){let i=r(t),o=r(e);if(i!==void 0&&o!==void 0)return [i,o]}let s=i=>i===void 0?null:typeof i=="object"&&i!==null?String(i):i;return [s(t),s(e)]}function Hn(t){let e=new zt(wi(t)).parse();return n=>ge(j(e,n))}var he=class extends Error{constructor(e){super(e),this.name="ODataUrlError";}};function st(t){let e=t.trim();if(e.startsWith("'"))return _i(e);let n=/^(guid|datetime|datetimeoffset|time|X|binary)'(.*)'$/is.exec(e);return n?n[2].replace(/''/g,"'"):/^-?\d+(\.\d+)?[mMdDfFlL]?$/.test(e)?Number(e.replace(/[mMdDfFlL]$/,"")):e==="true"?true:e==="false"?false:e}function _i(t){if(!t.endsWith("'")||t.length<2)throw new he(`Zeichenkette ohne schlie\xDFendes Apostroph: ${t}`);return t.slice(1,-1).replace(/''/g,"'")}function ki(t){let e=Yt(t);return Ii(e,",").map(r=>{let s=Di(r,"=");return s===-1?{value:st(r)}:{name:r.slice(0,s).trim(),value:st(r.slice(s+1))}})}function Xt(t){let e=t.replace(/^\/+|\/+$/g,""),n=e.indexOf("("),r=e.indexOf("/"),s,i,o;if(n!==-1&&(r===-1||n<r)){s=e.slice(0,n);let l=Oi(e,n);i=ki(e.slice(n+1,l)),o=e.slice(l+1);}else r!==-1?(s=e.slice(0,r),o=e.slice(r)):(s=e,o="");let a=o.split("/").map(l=>Yt(l)).filter(l=>l.length>0),c={entitySet:Yt(s),segments:a};return i&&(c.key=i),c}function Oi(t,e){let n=false;for(let r=e+1;r<t.length;r++){let s=t[r];if(s==="'"){n=!n;continue}if(!n&&s===")")return r}throw new he(`Schlie\xDFende Klammer fehlt in: ${t}`)}function Ii(t,e){let n=[],r="",s=false;for(let i=0;i<t.length;i++){let o=t[i];if(o==="'"){s=!s,r+=o;continue}if(!s&&o===e){n.push(r),r="";continue}r+=o;}return n.push(r),n.filter(i=>i.trim().length>0)}function Di(t,e){let n=false;for(let r=0;r<t.length;r++)if(t[r]==="'")n=!n;else if(!n&&t[r]===e)return r;return -1}function Yt(t){try{return decodeURIComponent(t)}catch{return t}}var it="DEMO",Zt="demo",en="100",xe="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2",Ni="/sap/opu/odata4/",A=class extends Error{constructor(n,r,s,i={}){super(s);this.status=n;this.code=r;this.headers=i;}},ot=class{constructor(e={}){this.options=e;for(let n of rt)for(let r of n.entityTypes)this.types.set(r.set,{type:r,service:n});}data=Gt();versions=new Map;csrfToken=randomBytes(16).toString("base64url");cookie=`SAP_SESSIONID_DEMO_100=${randomBytes(12).toString("base64url")}`;types=new Map;nextNumber=new Map;reset(){this.data=Gt(),this.versions.clear(),this.nextNumber.clear();}rows(e){return this.data[e]??[]}handle(e){let r={base:this.options.baseUrl??`http://${e.headers.host??"127.0.0.1"}`,inBatch:false};try{return this.dispatch(e,r)}catch(s){return this.errorResponse(s)}}dispatch(e,n){let r=e.method.toUpperCase(),s=new URL(e.url,n.base),i=s.pathname.replace(/\/+$/,"")||"/",o=s.searchParams;n.inBatch||this.checkAuth(e.headers,o);let c=(e.headers["x-csrf-token"]??"").toLowerCase()==="fetch"?{"x-csrf-token":this.csrfToken,"set-cookie":`${this.cookie}; path=/`}:{},l=r==="HEAD",d=l?"GET":r,u=d==="POST"&&(e.headers["x-http-method"]??"").toUpperCase()==="MERGE"?"MERGE":d;if(u!=="GET"&&!n.inBatch&&e.headers["x-csrf-token"]!==this.csrfToken)throw new A(403,"HTTP/403","CSRF token validation failed",{"x-csrf-token":"Required"});let p;if(i===xe||i.startsWith(`${xe}/`))p=this.handleCatalog(u,i.slice(xe.length),o,n);else {if(i.startsWith(Ni))throw new A(404,"HTTP/404",`Resource not found for segment '${i}'`);{let f=rt.find(g=>i===g.path||i.startsWith(`${g.path}/`));if(!f)throw new A(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${i}'`);p=this.handleService(f,u,i.slice(f.path.length),o,e,n);}}return p.headers={...p.headers,...c},l&&(p.body=""),p}checkAuth(e,n){let r=e.authorization??"",s=`Basic ${Buffer.from(`${this.options.username??it}:${this.options.password??Zt}`).toString("base64")}`,i=a=>new A(401,"HTTP/401",a,{"www-authenticate":'Basic realm="SAP NetWeaver Application Server [DEMO/100]"'});if(r!==s)throw i("Anmeldung fehlgeschlagen");let o=n.get("sap-client");if(o!==(this.options.sapClient??en))throw i(o?`Mandant ${o} existiert nicht`:"Anmeldung fehlgeschlagen")}handleCatalog(e,n,r,s){if(e!=="GET")throw new A(405,"HTTP/405","Method not allowed on catalog");let i=n.replace(/^\/+/,"");if(i==="")return this.json(200,{d:{EntitySets:["ServiceCollection"]}});if(i==="$metadata")return this.xml(200,'<?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx"><edmx:DataServices m:DataServiceVersion="2.0" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"><Schema Namespace="CATALOGSERVICE" xmlns="http://schemas.microsoft.com/ado/2008/09/edm"><EntityType Name="Service"><Key><PropertyRef Name="ID"/></Key><Property Name="ID" Type="Edm.String" Nullable="false"/><Property Name="TechnicalServiceName" Type="Edm.String"/><Property Name="Title" Type="Edm.String"/><Property Name="Description" Type="Edm.String"/><Property Name="ServiceUrl" Type="Edm.String"/><Property Name="TechnicalServiceVersion" Type="Edm.String"/></EntityType><EntityContainer Name="CATALOGSERVICE_Entities" m:IsDefaultEntityContainer="true"><EntitySet Name="ServiceCollection" EntityType="CATALOGSERVICE.Service"/></EntityContainer></Schema></edmx:DataServices></edmx:Edmx>');let o=Xt(i);if(o.entitySet!=="ServiceCollection")throw new A(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${i}'`);let a=[...rt].map(u=>({__metadata:{id:`${s.base}${xe}/ServiceCollection('${u.technicalName}_0001')`,uri:`${s.base}${xe}/ServiceCollection('${u.technicalName}_0001')`,type:"CATALOGSERVICE.Service"},ID:`${u.technicalName}_0001`,TechnicalServiceName:u.technicalName,Title:u.title,Description:u.description,ServiceUrl:`${s.base}${u.path}`,TechnicalServiceVersion:"0001",MetadataUrl:`${s.base}${u.path}/$metadata`}));if(o.key){let u=String(o.key[0]?.value??""),m=a.find(p=>p.ID===u);if(!m)throw new A(404,"/IWFND/CM_MGW/002",`Service '${u}' not found`);return this.json(200,{d:m})}let c=Wn(r),{rows:l,count:d}=Jt(a,c);return this.json(200,{d:{results:l,...c.inlinecount==="allpages"?{__count:String(d)}:{}}})}handleService(e,n,r,s,i,o){let a=r.replace(/^\/+/,"");if(a===""){if(n!=="GET")throw new A(405,"HTTP/405","Method not allowed");return this.json(200,{d:{EntitySets:e.entityTypes.map(l=>l.set)}})}if(a==="$metadata")return this.xml(200,Un(e));if(a==="$batch"){if(n!=="POST")throw new A(405,"HTTP/405","Method not allowed on $batch");return this.handleBatch(e,i,o)}let c=e.functionImports.find(l=>l.name===a);if(c){if(n!==c.httpMethod&&!(n==="MERGE"&&c.httpMethod==="POST"))throw new A(405,"HTTP/405",`Function import ${c.name} requires ${c.httpMethod}`);return this.handleFunctionImport(e,c.name,s,o)}return this.handleEntity(e,n,a,s,i,o)}handleFunctionImport(e,n,r,s){let i=e.functionImports.find(a=>a.name===n),o={};for(let a of i.parameters){let c=r.get(a.name);if(c===null){if(!a.nullable)throw new A(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Parameter '${a.name}' is missing`);continue}o[a.name]=st(c);}if(n==="ReleaseSalesOrder"){let a=String(o.SalesOrder??""),c=this.rows("A_SalesOrder").find(l=>l.SalesOrder===a);if(!c)throw new A(404,"SD/001",`Sales order ${a} does not exist`);if(c.OverallSDProcessStatus==="C")throw new A(400,"SD/002",`Sales order ${a} is already completed and cannot be released`);return c.OverallSDProcessStatus="B",c.LastChangeDateTime=`/Date(${Date.now()})/`,this.bump("A_SalesOrder",c),this.json(200,{d:this.present(e,this.typeOf("A_SalesOrder"),c,s,{})})}if(n==="GetOpenSalesOrderCount"){let a=o.SoldToParty===void 0?void 0:String(o.SoldToParty),c=this.rows("A_SalesOrder").filter(l=>l.OverallSDProcessStatus!=="C"&&(a===void 0||l.SoldToParty===a)).length;return this.json(200,{d:{GetOpenSalesOrderCount:c}})}throw new A(501,"HTTP/501",`Function import ${n} not implemented`)}handleEntity(e,n,r,s,i,o){let a=i.body,c=i.headers["if-match"],l;try{l=Xt(r);}catch(h){throw h instanceof he?new A(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR",h.message):h}let d=e.entityTypes.find(h=>h.set===l.entitySet);if(!d)throw new A(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${l.entitySet}'`);let u=Wn(s),m=this.rows(d.set),p;if(l.key&&(p=this.find(d,l.key),!p))throw new A(404,"/IWBEP/CM_MGW_RT/020",`Resource not found for the segment '${d.set}'`);let f=[...l.segments],g=false;if(f[f.length-1]==="$count"&&(g=true,f.pop()),f.length>0){if(!p)throw new A(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR","Navigation requires a key");if(f.length>1)throw new A(501,"HTTP/501","Only one navigation level is supported");let h=d.navigations.find(y=>y.name===f[0]);if(!h)throw new A(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${f[0]}'`);let S=this.typeOf(h.target),T=this.related(p,h);if(h.multiplicity==="1"){if(p=T[0],!p)throw new A(404,"/IWBEP/CM_MGW_RT/020",`No ${h.target} for this ${d.set}`);d=S,m=[];}else m=T,p=void 0,d=S;}switch(n){case "GET":{if(p){let T=this.json(200,{d:this.present(e,d,p,o,u)});return T.headers.etag=this.etag(d,p),T}if(g){let T=Jt(m,{...u,top:void 0,skip:void 0,raw:u.raw});return {status:200,headers:{"content-type":"text/plain"},body:String(T.count)}}let{rows:h,count:S}=Jt(m,u);return this.json(200,{d:{results:h.map(T=>this.present(e,d,T,o,u)),...u.inlinecount==="allpages"?{__count:String(S)}:{}}})}case "POST":{if(p||l.key)throw new A(405,"HTTP/405","POST on a single entity is not allowed (use MERGE/PATCH)");if(d.creatable===false)throw new A(405,"HTTP/405",`${d.set} is not creatable`);let h=this.parseBody(a),S=this.create(d,h,o),T=this.json(201,{d:this.present(e,d,S,o,{expand:this.deepExpands(d,h)})});return T.headers.location=this.uri(o,e,d,S),T.headers.etag=this.etag(d,S),T}case "MERGE":case "PATCH":case "PUT":{if(!p)throw new A(405,"HTTP/405",`${n} requires a single entity`);if(d.updatable===false)throw new A(405,"HTTP/405",`${d.set} is not updatable`);this.checkIfMatch(d,p,c);let h=this.parseBody(a);return this.update(d,p,h,n==="PUT"),{status:204,headers:{etag:this.etag(d,p),dataserviceversion:"2.0"},body:""}}case "DELETE":{if(!p)throw new A(405,"HTTP/405","DELETE requires a single entity");if(d.deletable===false)throw new A(405,"HTTP/405",`${d.set} is not deletable`);return this.checkIfMatch(d,p,c),this.remove(d,p),{status:204,headers:{dataserviceversion:"2.0"},body:""}}default:throw new A(405,"HTTP/405",`Method ${n} not allowed`)}}parseBody(e){if(!e||e.trim()==="")throw new A(400,"/IWCOR/CX_DS_BODY_MISSING","Request body is empty");let n;try{n=JSON.parse(e);}catch{throw new A(400,"/IWCOR/CX_DS_JSON_SYNTAX_ERROR","Request body is not valid JSON")}if(n===null||typeof n!="object"||Array.isArray(n))throw new A(400,"/IWCOR/CX_DS_JSON_SYNTAX_ERROR","Request body must be a JSON object");let r=n;return "d"in r&&typeof r.d=="object"&&r.d!==null&&Object.keys(r).length===1?r.d:r}create(e,n,r,s={}){let i={},o=[];for(let[a,c]of Object.entries(n)){if(a==="__metadata")continue;let l=e.navigations.find(u=>u.name===a);if(l){if(l.multiplicity!=="*")continue;let u=Array.isArray(c)?c:c&&typeof c=="object"&&Array.isArray(c.results)?c.results:[];o.push({nav:l,children:u});continue}let d=e.properties.find(u=>u.name===a);if(!d)throw new A(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Property '${a}' is invalid`);i[a]=this.coerce(d.name,d.type,c);}Object.assign(i,s);for(let a of e.keys)(i[a]===void 0||i[a]==="")&&(i[a]=this.nextKey(e,a,i));if(this.find(e,e.keys.map(a=>({name:a,value:i[a]}))))throw new A(400,"/IWBEP/CX_MGW_BUSI_EXCEPTION",`${e.set} with this key already exists`);for(let a of e.properties)i[a.name]===void 0&&(i[a.name]=this.emptyValue(a.type));this.defaults(e,i),this.rows(e.set).push(i),this.bump(e.set,i);for(let{nav:a,children:c}of o){let l=this.typeOf(a.target),d={};for(let[u,m]of a.join)d[m]=i[u];for(let u of c)this.create(l,u,r,d);}return this.recalculate(e,i),i}update(e,n,r,s){let i={};for(let[o,a]of Object.entries(r)){if(o==="__metadata"||e.navigations.some(l=>l.name===o))continue;let c=e.properties.find(l=>l.name===o);if(!c)throw new A(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Property '${o}' is invalid`);if(e.keys.includes(o)&&this.coerce(o,c.type,a)!==n[o])throw new A(400,"/IWBEP/CX_MGW_BUSI_EXCEPTION",`Key property '${o}' cannot be changed`);i[o]=this.coerce(c.name,c.type,a);}if(s)for(let o of e.properties)!e.keys.includes(o.name)&&!(o.name in i)&&(n[o.name]=this.emptyValue(o.type));if(Object.assign(n,i),(e.set==="A_SalesOrder"||e.set==="A_SalesOrderItem")&&(n.LastChangeDateTime=`/Date(${Date.now()})/`,e.set==="A_SalesOrderItem")){let o=this.rows("A_SalesOrder").find(a=>a.SalesOrder===n.SalesOrder);o&&this.recalculate(this.typeOf("A_SalesOrder"),o);}this.bump(e.set,n);}remove(e,n){let r=this.rows(e.set),s=r.indexOf(n);s!==-1&&r.splice(s,1),this.versions.delete(this.versionKey(e.set,n));for(let i of e.navigations){if(i.multiplicity!=="*")continue;let o=this.typeOf(i.target);for(let a of this.related(n,i))this.remove(o,a);}}checkIfMatch(e,n,r){if(r===void 0||r.trim()==="*")return;let s=this.etag(e,n);if(!r.split(",").map(o=>o.trim()).map(o=>o.startsWith("W/")?o:`W/${o}`).includes(s))throw new A(412,"/IWBEP/CM_MGW_RT/021","The entity has been changed by another user; reload and retry (ETag mismatch)")}handleBatch(e,n,r){let s=n.headers["content-type"]??"",i=/boundary=("?)([^";]+)\1/i.exec(s)?.[2];if(!i||!n.body)throw new A(400,"/IWCOR/CX_DS_BATCH","multipart/mixed body with boundary expected");let o=`batchresponse_${randomUUID()}`,a=[];for(let c of qn(n.body,i)){let l=/content-type:\s*multipart\/mixed;\s*boundary=("?)([^";\r\n]+)\1/i.exec(c);if(l){let d=l[2],u=c.indexOf(`\r
|
|
42
42
|
\r
|
|
43
43
|
`),m=u===-1?"":c.slice(u+4),p=this.snapshot(),f=[],g;for(let h of qn(m,d)){let S=this.runBatchPart(e,h,r);if(S.status>=400){g=S;break}f.push(S);}if(g)this.restore(p),a.push(`--${o}\r
|
|
44
44
|
${Qt(g)}`);else {let h=`changesetresponse_${randomUUID()}`,S=f.map(T=>`--${h}\r
|
|
@@ -89,7 +89,7 @@ Auth types: basic, user-basic (personal SAP login per request \u2014 the caller
|
|
|
89
89
|
Put the token into an n8n Bearer Auth credential on the MCP Client node.
|
|
90
90
|
`,L=class extends Error{constructor(n,r=1){super(n);this.exitCode=r;}};async function sr(t,e){let n=go(t);try{if(n.flags.help)return e.stdout(dt),0;let[r,s,...i]=n.positional;if(r==="destinations")switch(s){case "list":return ho(n,e);case "add":return So(i[0],n,e);case "remove":return To(i[0],n,e);default:throw new L(`Unknown command "destinations ${s??""}".
|
|
91
91
|
|
|
92
|
-
${dt}`,2)}if(r==="tokens")switch(s){case "list":return Ao(n,e);case "issue":return bo(i[0],n,e);case "revoke":return
|
|
92
|
+
${dt}`,2)}if(r==="tokens")switch(s){case "list":return Ao(n,e);case "issue":return bo(i[0],n,e);case "revoke":return vo(i[0],n,e);default:throw new L(`Unknown command "tokens ${s??""}".
|
|
93
93
|
|
|
94
94
|
${dt}`,2)}throw new L(dt,2)}catch(r){return r instanceof L?(e.stderr(`${r.message}
|
|
95
95
|
`),r.exitCode):r instanceof P?(e.stderr(`${r.message}
|
|
@@ -101,12 +101,12 @@ ${dt}`,2)}throw new L(dt,2)}catch(r){return r instanceof L?(e.stderr(`${r.messag
|
|
|
101
101
|
`),0}function Ao(t,e){let n=Ae(t,e).listTokens().map(r=>{let s=[];return r.policy?.readOnly&&s.push("read-only"),r.policy?.tiers&&s.push(`tiers=${r.policy.tiers.join(",")}`),[r.label??"-",r.destination,r.hash,s.length>0?s.join(" "):"full",r.revoked?"revoked":"active",r.createdAt??"-"]});return e.stdout(`${ir(["LABEL","DESTINATION","HASH","POLICY","STATUS","CREATED"],n)}
|
|
102
102
|
`),0}function bo(t,e,n){if(!t)throw new L("Usage: tokens issue <destination> [--label ...]",2);let r=Ae(e,n),s=lt(e,"tiers"),i=lt(e,"label"),{token:o,hash:a}=r.issueToken(t,{label:i,readOnly:!!e.flags["read-only"],tiers:s===void 0?void 0:s.split(",")});return n.stdout(`${o}
|
|
103
103
|
`),n.stderr([`Token issued for destination "${t}"${i?` (${i})`:""} \u2014 hash ${re(a)}\u2026 stored in ${r.filePath}.`,"The plain token above is shown ONCE and cannot be recovered; put it into an n8n Bearer Auth credential","(Authorization: Bearer <token>) on the MCP Client node. Revoke with: tokens revoke <label|hash-prefix>.",""].join(`
|
|
104
|
-
`)),0}function
|
|
104
|
+
`)),0}function vo(t,e,n){if(!t)throw new L("Usage: tokens revoke <label | hash-prefix>",2);let{hash:r,label:s,alreadyRevoked:i}=Ae(e,n).revokeToken(t);return i?(n.stderr(`Token ${r} was already revoked.
|
|
105
105
|
`),0):(n.stderr(`Token ${r}${s?` (${s})`:""} revoked. A running server rejects it from the next request on.
|
|
106
106
|
`),0)}function ir(t,e){let n=[t,...e],r=t.map((s,i)=>Math.max(...n.map(o=>(o[i]??"").length)));return n.map(s=>s.map((i,o)=>(i??"").padEnd(r[o])).join(" ").trimEnd()).join(`
|
|
107
107
|
`)}var se="default",ko=250,or=1e3,be=class t{source;filePath;current;currentRaw;currentVersion=1;env;envDefault;warn;observeFile;warnedFileWins=false;watcher;polling=false;startupRecheck;sighupHandler;debounceTimer;reloadInFlight;reloadQueued=false;changeListeners=new Set;errorListeners=new Set;constructor(e){this.source=e.source,this.filePath=e.filePath,this.current=e.config,this.currentRaw=e.raw,this.env=e.env,this.envDefault=e.envDefault,this.warn=e.onWarn??(()=>{}),this.observeFile=e.watchFile??true;}static fromEnv(e=process.env){let n={...Me(e),name:se};return new t({source:"env",config:{destinations:new Map([[se,n]]),tokens:[]},env:e})}static async fromFile(e,n={}){let r=n.env??process.env,s=resolve(e),i=r.SAP_BASE_URL?{...Me(r),name:se}:void 0,o=new t({source:"file",filePath:s,config:{destinations:new Map,tokens:[]},env:r,envDefault:i,onWarn:n.onWarn,watchFile:n.watchFile}),a=await o.readAndParse();if(!a.ok){if(a.missing&&i)return o.warn(`${s} existiert (noch) nicht \u2014 der Server startet mit der SAP_*-Umgebung als Destination "default" und \xFCbernimmt die Datei, sobald sie angelegt wird.`),o.current={destinations:new Map([[se,i]]),tokens:[]},o;if(a.missing&&n.allowMissing)return o.warn(`${s} existiert (noch) nicht und es ist keine SAP_*-Umgebung gesetzt \u2014 der Server startet ohne Destination und \xFCbernimmt die Datei, sobald sie angelegt wird (z. B. \xFCber die Admin-API).`),o;throw new Error(`Ung\xFCltige Destinations-Datei ${s}:
|
|
108
108
|
${a.errors.map(c=>` - ${c}`).join(`
|
|
109
|
-
`)}`)}return o.current=a.value,o.currentRaw=a.raw,o}get version(){return this.currentVersion}get(e){return this.current.destinations.get(e)}names(){return [...this.current.destinations.keys()]}defaultDestination(){let e=this.current.destinations.get(se);if(e)return e;if(this.current.destinations.size===1)return this.current.destinations.values().next().value}tokens(){return this.current.tokens}onChange(e){return this.changeListeners.add(e),()=>this.changeListeners.delete(e)}onError(e){return this.errorListeners.add(e),()=>this.errorListeners.delete(e)}reload(){return this.source==="env"?Promise.resolve({ok:true,changed:false}):this.reloadInFlight?(this.reloadQueued=true,this.reloadInFlight):(this.reloadInFlight=this.doReload().finally(()=>{this.reloadInFlight=void 0,this.reloadQueued&&(this.reloadQueued=false,this.reload());}),this.reloadInFlight)}watch(){if(!(this.source==="env"||!this.filePath)&&(this.sighupHandler||(this.sighupHandler=()=>{this.reload();},process.on("SIGHUP",this.sighupHandler)),this.observeFile&&!this.polling&&(this.polling=true,watchFile(this.filePath,{persistent:false,interval:or},(e,n)=>{(e.mtimeMs!==n.mtimeMs||e.size!==n.size)&&this.scheduleReload();}),this.startupRecheck=setTimeout(()=>{this.startupRecheck=void 0,this.scheduleReload();},or+500),this.startupRecheck.unref()),this.observeFile&&!this.watcher))try{this.watcher=watch(dirname(this.filePath),{persistent:!1},()=>{this.scheduleReload();}),this.watcher.on("error",e=>{this.warn(`Dateibeobachtung f\xFCr ${this.filePath} ausgefallen (${e.message}); SIGHUP l\xE4dt weiterhin neu.`),this.watcher=void 0;});}catch(e){this.warn(`Dateibeobachtung f\xFCr ${this.filePath} nicht m\xF6glich (${e instanceof Error?e.message:String(e)}); SIGHUP l\xE4dt weiterhin neu.`);}}close(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=void 0),this.startupRecheck&&(clearTimeout(this.startupRecheck),this.startupRecheck=void 0),this.watcher&&(this.watcher.close(),this.watcher=void 0),this.polling&&this.filePath&&(unwatchFile(this.filePath),this.polling=false),this.sighupHandler&&(process.off("SIGHUP",this.sighupHandler),this.sighupHandler=void 0),this.changeListeners.clear(),this.errorListeners.clear();}scheduleReload(){this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.debounceTimer=void 0,this.reload();},ko),this.debounceTimer.unref();}async doReload(){let e=await this.readAndParse();if(!e.ok){if(e.missing&&this.currentRaw===void 0)return {ok:true,changed:false};for(let r of this.errorListeners)r(e.errors);return {ok:false,errors:e.errors}}if(e.raw===this.currentRaw)return {ok:true,changed:false};this.current=e.value,this.currentRaw=e.raw,this.currentVersion+=1;let n={version:this.currentVersion,names:this.names(),tokenCount:this.current.tokens.length};for(let r of this.changeListeners)r(n);return {ok:true,changed:true}}async readAndParse(){if(!this.filePath)return {ok:false,errors:["Kein Dateipfad konfiguriert."]};let e;try{e=await readFile(this.filePath,"utf8");}catch(s){let i=s.code,o=i==="ENOENT"?"Datei nicht gefunden":s.message;return {ok:false,errors:[`${this.filePath}: ${o}`],missing:i==="ENOENT"}}let n;try{n=JSON.parse(e);}catch(s){return {ok:false,errors:[`${this.filePath}: kein g\xFCltiges JSON (${s instanceof Error?s.message:String(s)})`]}}let r=at(n,this.env);return r.ok?(this.envDefault&&(r.value.destinations.has(se)?this.warnedFileWins||(this.warnedFileWins=true,this.warn(`Sowohl ${this.filePath} als auch die SAP_*-Umgebung definieren die Destination "default" \u2014 die Datei gilt, die Umgebung wird ignoriert.`)):r.value.destinations.set(se,this.envDefault)),{ok:true,value:r.value,raw:e}):r}};var we=class{buffer=[];maxSize;constructor(e=100){this.maxSize=e;}add(e){this.buffer.length>=this.maxSize&&this.buffer.shift(),this.buffer.push(e);}get(e){return this.buffer.find(n=>n.docnum===e)}list(){return [...this.buffer]}get count(){return this.buffer.length}};var Io=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",parseTagValue:false,processEntities:false,htmlEntities:false,isArray:t=>t==="IDOC"||t.startsWith("E1")||t.startsWith("E2")}),$e=new Set(["@_SEGMENT","@_BEGIN"]);function rn(t,e){return !!(t.startsWith("E1")||t.startsWith("E2")||typeof e=="object"&&e!==null&&"@_SEGMENT"in e)}function ar(t){let e=[];for(let[n,r]of Object.entries(t)){if($e.has(n)||!rn(n,r))continue;let s=Array.isArray(r)?r:[r];for(let i of s){if(typeof i!="object"||i===null)continue;let o={},a=[];for(let[c,l]of Object.entries(i))if(!$e.has(c))if(rn(c,l)){let d=Array.isArray(l)?l:[l];for(let u of d){if(typeof u!="object"||u===null)continue;let m={},p=ar(u);for(let[f,g]of Object.entries(u))$e.has(f)||!rn(f,g)&&typeof g!="object"&&(m[f]=String(g));a.push({name:c,fields:m,...p.length>0?{children:p}:{}});}}else typeof l!="object"&&(o[c]=String(l));e.push({name:n,fields:o,...a.length>0?{children:a}:{}});}}return e}function cr(t){let e=Io.parse(t),n=Object.keys(e).find(u=>u!=="?xml");if(!n)throw new Error("Invalid IDoc XML: no root element found");let r=n,i=e[n].IDOC;if(!i||!Array.isArray(i)||i.length===0)throw new Error("Invalid IDoc XML: no IDOC element found");let o=i[0],a=o.EDI_DC40,c={};if(a){let u=Array.isArray(a)?a[0]:a;for(let[m,p]of Object.entries(u))!$e.has(m)&&typeof p!="object"&&(c[m]=String(p));}let l={};for(let[u,m]of Object.entries(o))u==="EDI_DC40"||$e.has(u)||(l[u]=m);let d=ar(l);return {idocType:r,controlRecord:c,segments:d}}var Do=10*1024*1024,ut=class extends Error{};function No(t,e){return new Promise((n,r)=>{let s=[],i=0,o=false;t.on("data",a=>{if(!o){if(i+=a.length,i>e){o=true,s.length=0,r(new ut(`Request body exceeds ${e} bytes`));return}s.push(a);}}),t.on("end",()=>{o||n(Buffer.concat(s).toString("utf-8"));}),t.on("error",a=>{o||r(a);});})}function xo(t,e){if(e.length===0||typeof t!="string")return false;if(t.startsWith("Bearer "))return Ue(t.slice(7),e);if(t.startsWith("Basic ")){let n=Buffer.from(t.slice(6),"base64").toString("utf-8"),r=n.indexOf(":");return r===-1?false:Ue(n.slice(r+1),e)}return false}function Mo(t){if(typeof t!="string")return true;let e=(t.split(";")[0]??"").trim().toLowerCase();return e.endsWith("xml")||e==="application/x-sap.idoc"}function pt(t,e={}){let n=e.maxBodyBytes??Do;return (r,s)=>{if(r.method!=="POST"){s.writeHead(405,{"Content-Type":"text/plain"}),s.end("Method Not Allowed");return}if(e.secret!==void 0&&!xo(r.headers.authorization,e.secret)){s.writeHead(401,{"Content-Type":"text/plain","WWW-Authenticate":'Basic realm="idoc-webhook"'}),s.end("Unauthorized"),r.destroy();return}if(!Mo(r.headers["content-type"])){s.writeHead(415,{"Content-Type":"text/plain"}),s.end("Unsupported Media Type: expected XML"),r.destroy();return}No(r,n).then(i=>{let{idocType:o,controlRecord:a,segments:c}=cr(i),l={docnum:a.DOCNUM||`UNKNOWN_${Date.now()}`,idocType:o,mesType:a.MESTYP||"",receivedAt:new Date,rawXml:i,parsed:{controlRecord:a,segments:c}};t.add(l),s.writeHead(200,{"Content-Type":"text/plain"}),s.end("OK");}).catch(i=>{if(i instanceof ut){s.writeHead(413,{"Content-Type":"text/plain"}),s.end("Payload Too Large"),r.destroy();return}s.writeHead(400,{"Content-Type":"text/plain"}),s.end(`Parse error: ${i instanceof Error?i.message:String(i)}`);});}}function dr(t,e,n={}){let r=pt(e,n);return new Promise((s,i)=>{let o=createServer((c,l)=>{c.method==="POST"&&c.url==="/idoc"?r(c,l):(l.writeHead(404,{"Content-Type":"text/plain"}),l.end("Not Found"));}),a=()=>{let c=o.address(),l=typeof c=="object"&&c!==null?c.port:t;s({port:l,close:()=>new Promise((d,u)=>{o.close(m=>m?u(m):d());})});};n.host?o.listen(t,n.host,a):o.listen(t,a),o.on("error",i);})}var ur=1;function mt(t){if(!t.directory)return [];let e=join(t.directory,t.fileName),n;try{n=readFileSync(e,"utf-8");}catch{return []}try{let r=JSON.parse(n);if(r?.version!==ur||!Array.isArray(r.entries))return t.onWarn?.(`Cache-Datei ${e} hat ein unbekanntes Format und wird verworfen.`),[];let s=Date.now()-t.ttlMs;return r.entries.filter(i=>i&&typeof i.key=="string"&&typeof i.storedAt=="number"&&i.storedAt>s)}catch(r){return t.onWarn?.(`Cache-Datei ${e} ist unlesbar und wird verworfen. Der Cache wird aus SAP neu aufgebaut.`,r),[]}}function ft(t,e){if(!e.directory)return;let n=join(e.directory,e.fileName),r=`${n}.tmp`;try{mkdirSync(dirname(n),{recursive:!0}),writeFileSync(r,JSON.stringify({version:ur,entries:t}),"utf-8"),renameSync(r,n);}catch(s){e.onWarn?.(`Cache konnte nicht nach ${n} geschrieben werden. Der Betrieb l\xE4uft weiter, nach einem Neustart ist der Cache aber leer.`,s);try{unlinkSync(r);}catch{}}}var Vo=30,Wo="catalog-cache.json",pr="catalog",G=class{cache=null;ttlMs;key;persistence;constructor(e={}){let n=typeof e=="number"?{ttlMinutes:e}:e;this.ttlMs=(n.ttlMinutes??Vo)*60*1e3;let r=(n.baseUrl??"").replace(/\/+$/,"").toLowerCase();if(this.key=r?`${r}|${pr}`:pr,n.persistDir){this.persistence={directory:n.persistDir,fileName:Wo,ttlMs:this.ttlMs,...n.onWarn?{onWarn:n.onWarn}:{}};let s=mt(this.persistence).find(i=>i.key===this.key);s&&(this.cache={entries:s.value,timestamp:s.storedAt});}}get(){return this.cache?Date.now()-this.cache.timestamp>this.ttlMs?(this.cache=null,null):this.cache.entries:null}set(e){this.cache={entries:e,timestamp:Date.now()},this.persist();}clear(){this.cache=null,this.persist();}persist(){this.persistence&&ft(this.cache?[{key:this.key,value:this.cache.entries,storedAt:this.cache.timestamp}]:[],this.persistence);}};var sn=0;function mr(t){let e=t.docnum??`${Date.now().toString().padStart(13,"0")}${String(sn++).padStart(3,"0")}`;sn>=1e3&&(sn=0);let n=t.mesType??t.idocType.replace(/\d+$/,"");return {TABNAM:"EDI_DC40",DOCNUM:e,DIRECT:"2",IDOCTYP:t.idocType,MESTYP:n,SNDPOR:t.sndpor,SNDPRT:t.sndprt??"LS",SNDPRN:t.sndprn,RCVPOR:t.rcvpor,RCVPRT:t.rcvprt??"LS",RCVPRN:t.rcvprn,...t.extra}}function qo(t,e,n){return e.required&&n===""?{segmentName:t,fieldName:e.name,expectedType:e.dataType,expectedLength:e.length,actualValue:n,violation:"Required field is empty"}:n===""?null:(e.dataType==="CHAR"||e.dataType==="NUMC")&&n.length>e.length?{segmentName:t,fieldName:e.name,expectedType:e.dataType,expectedLength:e.length,actualValue:n,violation:`Field value exceeds max length ${e.length} (actual: ${n.length})`}:e.dataType==="NUMC"&&!/^\d*$/.test(n)?{segmentName:t,fieldName:e.name,expectedType:e.dataType,expectedLength:e.length,actualValue:n,violation:"NUMC field must contain only digits"}:e.dataType==="DATS"&&!/^\d{8}$/.test(n)?{segmentName:t,fieldName:e.name,expectedType:e.dataType,expectedLength:e.length,actualValue:n,violation:"DATS field must be in YYYYMMDD format"}:e.dataType==="TIMS"&&!/^\d{6}$/.test(n)?{segmentName:t,fieldName:e.name,expectedType:e.dataType,expectedLength:e.length,actualValue:n,violation:"TIMS field must be in HHMMSS format"}:null}function Ko(t,e){return t.filter(n=>n.name===e).length}function fr(t,e,n){let r=new Set;for(let s of t){if(r.has(s.name))continue;r.add(s.name);let i=e.get(s.name);if(!i)continue;let o=Ko(t,s.name);o>i.maxOccurrence&&n.push({segmentName:s.name,fieldName:"",expectedType:"",expectedLength:i.maxOccurrence,actualValue:String(o),violation:`Segment count ${o} exceeds maxOccurrence ${i.maxOccurrence}`});}for(let s of t){let i=e.get(s.name);if(!i){n.push({segmentName:s.name,fieldName:"",expectedType:"",expectedLength:0,actualValue:"",violation:`Unknown segment '${s.name}' not found in metadata`});continue}for(let o of i.fields){let a=s.fields[o.name]??"",c=qo(s.name,o,a);c&&n.push(c);}s.children&&s.children.length>0&&fr(s.children,e,n);}}function gr(t,e){if(!e)return [];let n=new Map;for(let s of e.segments)n.set(s.name,s);let r=[];return fr(t,n,r),r}var zo=new XMLBuilder({ignoreAttributes:false,attributeNamePrefix:"@_",format:true,suppressEmptyNode:false});function hr(t){let e={"@_SEGMENT":"1"};for(let[n,r]of Object.entries(t.fields))e[n]=r;if(t.children)for(let n of t.children){let r=hr(n),s=e[n.name];s?Array.isArray(s)?s.push(r):e[n.name]=[s,r]:e[n.name]=r;}return e}function yr(t,e,n){let r={};for(let i of n){let o=hr(i),a=r[i.name];a?Array.isArray(a)?a.push(o):r[i.name]=[a,o]:r[i.name]=o;}let s={"?xml":{"@_version":"1.0","@_encoding":"UTF-8"},[t]:{IDOC:{"@_BEGIN":"1",EDI_DC40:{"@_SEGMENT":"1",...e},...r}}};return zo.build(s)}var gt=class{constructor(e,n,r){this.httpClient=e;this.sapClient=n;this.partnerDefaults=r;}async send(e){let n={...this.partnerDefaults,...e.partnerOverrides},r=mr({idocType:e.idocType,sndpor:n.sndpor??"",sndprn:n.sndprn??"",rcvpor:n.rcvpor??"",rcvprn:n.rcvprn??"",sndprt:n.sndprt,rcvprt:n.rcvprt,mesType:n.mesType,docnum:n.docnum,extra:n.extra});if(e.metadata){let u=gr(e.segments,e.metadata);if(u.length>0)return {success:false,docnum:r.DOCNUM,message:"Validation failed",violations:u}}let s=yr(e.idocType,r,e.segments),i="/sap/bc/idoc_xml";this.sapClient&&(i+=`?sap-client=${this.sapClient}`);let o=await this.httpClient.request({method:"post",url:i,data:s,headers:{"Content-Type":"text/xml"}}),a=typeof o.data=="string"?o.data:String(o.data);if(a.includes("<title>IDoc-XML-inbound ok</title>"))return {success:true,docnum:r.DOCNUM,message:"IDoc sent successfully"};let d=a.match(/<title>(.*?)<\/title>/)?.[1]??"IDoc send failed";return {success:false,docnum:r.DOCNUM,message:d}}};var ht=30,Yo=5e3;function Xo(t,e,n,r){let s=t?._meta?.progressToken,i=t?.sendNotification;if(s===void 0||typeof i!="function"||r.afterMs<=0)return ()=>{};let a=setInterval(()=>{let c=Date.now()-n;i({method:"notifications/progress",params:{progressToken:s,progress:Math.round(c/1e3),...r.budgetMs?{total:Math.round(r.budgetMs/1e3)}:{},message:`${e.toolName}: ${Math.round(c/1e3)} s, ${e.sapCalls.count} SAP-Aufruf(e)`}}).catch(()=>{});},r.afterMs);return a.unref?.(),()=>clearInterval(a)}var Jo="SAP_TLS_VERIFY=false: Die Zertifikatspr\xFCfung ist abgeschaltet \u2014 die Verbindung zu SAP ist nicht gegen einen Angreifer in der Leitung gesch\xFCtzt. Richtiger Weg: Root-CA \xFCber SAP_CA_CERT (bzw. NODE_EXTRA_CA_CERTS) einbinden und SAP_TLS_VERIFY wieder entfernen.";function Qo(t){return {...t,content:[...t.content,{type:"text",text:`\u26A0\uFE0F ${Jo}`}]}}function Zo(t,e,n,r,s){return {content:[{type:"text",text:JSON.stringify({truncated:true,reason:`Zeitbudget von ${Math.round(s/1e3)} s \xFCberschritten \u2014 der Aufruf wurde abgebrochen, damit die Verbindung nicht h\xE4ngen bleibt.`,hint:"Grenzen Sie die Anfrage ein (top/skip, filter, search) oder erh\xF6hen Sie --tool-timeout. Der Wert sollte unter der kleinsten Client-Grenze bleiben (Cloudflare 100 s, n8n-MCP-Transport 300 s).",tool:t,correlationId:e,elapsedMs:n,sapCalls:r},null,2)}],isError:true}}function ea(t,e){return {content:[{type:"text",text:JSON.stringify({aborted:true,reason:"Der Client hat die Verbindung verworfen; die Arbeit wurde eingestellt.",tool:t,correlationId:e})}],isError:true}}function Sr(t,e,n={}){let{toolTimeoutMs:r,logger:s,tlsVerifyDisabled:i}=n,o=n.progressAfterMs??Yo;return async(a,c)=>{let l=Pn(),{signal:d,deadline:u}=kn(c?.signal,r),m={correlationId:l,toolName:t,signal:d,deadline:u,sapCalls:{count:0}},p=Date.now();return Rn(m,async()=>{let f=Xo(c,m,p,{afterMs:o,...r?{budgetMs:r}:{}}),g="ok",h,S=(async()=>e(a,c))();S.catch(()=>{});try{h=await Promise.race([S,ta(d,t)]),h?.isError&&(g="error");}catch(y){if(y instanceof $)g=y.timedOut?"timeout":"aborted",h=y.timedOut?Zo(t,l,Date.now()-p,m.sapCalls.count,r??0):ea(t,l);else {g="error";let b=y instanceof Error?y.message:String(y);h={content:[{type:"text",text:JSON.stringify({error:b,tool:t,correlationId:l})}],isError:true};}}g==="ok"&&d?.aborted&&(g=d.reason?.name==="TimeoutError"?"timeout":"aborted"),f();let T=i?Qo(h):h;return s?.info({correlationId:l,tool:t,outcome:g,durationMs:Date.now()-p,sapCalls:m.sapCalls.count,resultBytes:na(h),...r?{budgetMs:r}:{}},"MCP tool call"),T})}}function ta(t,e){return t?new Promise((n,r)=>{let s=()=>{let i=t.reason?.name==="TimeoutError";r(new $(i,e));};if(t.aborted){s();return}t.addEventListener("abort",s,{once:true});}):new Promise(()=>{})}function na(t){if(!t?.content)return 0;let e=0;for(let n of t.content)typeof n?.text=="string"&&(e+=Buffer.byteLength(n.text,"utf-8"));return e}var ra=1440*60*1e3,sa="metadata-cache.json",k=class{cache=new Map;ttlMs;baseUrl;persistence;constructor(e={}){if(this.ttlMs=e.ttlMs??ra,this.baseUrl=ia(e.baseUrl),e.persistDir){this.persistence={directory:e.persistDir,fileName:sa,ttlMs:this.ttlMs,...e.onWarn?{onWarn:e.onWarn}:{}};for(let n of mt(this.persistence))this.cache.set(n.key,{value:n.value,storedAt:n.storedAt});}}get size(){return this.cache.size}get(e){let n=this.key(e),r=this.cache.get(n);if(r){if(Date.now()-r.storedAt>this.ttlMs){this.cache.delete(n);return}return r.value}}set(e,n){this.cache.set(this.key(e),{value:n,storedAt:Date.now()}),this.persist();}has(e){return this.get(e)!==void 0}clear(){this.cache.clear(),this.persist();}key(e){let n=e.replace(/\/+$/,"");return this.baseUrl?`${this.baseUrl}|${n}`:n}persist(){this.persistence&&ft([...this.cache.entries()].map(([e,n])=>({key:e,value:n.value,storedAt:n.storedAt})),this.persistence);}};function ia(t){return t?t.replace(/\/+$/,"").toLowerCase():""}var Tr="0.0.0-unknown",Ar="guniweb-sap-mcp";function la(t){if(typeof t!="string"||t.length===0)return Tr;let e=resolve(t);for(;;){try{let r=readFileSync(join(e,"package.json"),"utf-8"),s=JSON.parse(r);if(s.name===Ar&&typeof s.version=="string")return s.version}catch{}let n=dirname(e);if(n===e)return Tr;e=n;}}var br=la(import.meta.dirname),wr=Ar;function vr(t){t.registerPrompt("nl-to-odata-query",{title:"Natural Language to OData Query",description:"Convert a natural language question into a correct OData query against an SAP service. Reads entity schema for context, applies V2/V4 syntax rules, and enforces read-only by default.",argsSchema:{serviceUrl:z$2.string().describe("OData service URL, e.g., /sap/opu/odata/sap/API_BUSINESS_PARTNER"),entitySet:z$2.string().describe("Entity set to query, e.g., A_BusinessPartner"),question:z$2.string().describe('Natural language question, e.g., "show me all customers in Germany"'),allowWrite:z$2.boolean().optional().describe("Allow write operations (create/update/delete). Default: false (read-only)")}},async({serviceUrl:e,entitySet:n,question:r,allowWrite:s})=>{let i=s===true?"":`
|
|
109
|
+
`)}`)}return o.current=a.value,o.currentRaw=a.raw,o}get version(){return this.currentVersion}get(e){return this.current.destinations.get(e)}names(){return [...this.current.destinations.keys()]}defaultDestination(){let e=this.current.destinations.get(se);if(e)return e;if(this.current.destinations.size===1)return this.current.destinations.values().next().value}tokens(){return this.current.tokens}onChange(e){return this.changeListeners.add(e),()=>this.changeListeners.delete(e)}onError(e){return this.errorListeners.add(e),()=>this.errorListeners.delete(e)}reload(){return this.source==="env"?Promise.resolve({ok:true,changed:false}):this.reloadInFlight?(this.reloadQueued=true,this.reloadInFlight):(this.reloadInFlight=this.doReload().finally(()=>{this.reloadInFlight=void 0,this.reloadQueued&&(this.reloadQueued=false,this.reload());}),this.reloadInFlight)}watch(){if(!(this.source==="env"||!this.filePath)&&(this.sighupHandler||(this.sighupHandler=()=>{this.reload();},process.on("SIGHUP",this.sighupHandler)),this.observeFile&&!this.polling&&(this.polling=true,watchFile(this.filePath,{persistent:false,interval:or},(e,n)=>{(e.mtimeMs!==n.mtimeMs||e.size!==n.size)&&this.scheduleReload();}),this.startupRecheck=setTimeout(()=>{this.startupRecheck=void 0,this.scheduleReload();},or+500),this.startupRecheck.unref()),this.observeFile&&!this.watcher))try{this.watcher=watch(dirname(this.filePath),{persistent:!1},()=>{this.scheduleReload();}),this.watcher.on("error",e=>{this.warn(`Dateibeobachtung f\xFCr ${this.filePath} ausgefallen (${e.message}); SIGHUP l\xE4dt weiterhin neu.`),this.watcher=void 0;});}catch(e){this.warn(`Dateibeobachtung f\xFCr ${this.filePath} nicht m\xF6glich (${e instanceof Error?e.message:String(e)}); SIGHUP l\xE4dt weiterhin neu.`);}}close(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=void 0),this.startupRecheck&&(clearTimeout(this.startupRecheck),this.startupRecheck=void 0),this.watcher&&(this.watcher.close(),this.watcher=void 0),this.polling&&this.filePath&&(unwatchFile(this.filePath),this.polling=false),this.sighupHandler&&(process.off("SIGHUP",this.sighupHandler),this.sighupHandler=void 0),this.changeListeners.clear(),this.errorListeners.clear();}scheduleReload(){this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.debounceTimer=void 0,this.reload();},ko),this.debounceTimer.unref();}async doReload(){let e=await this.readAndParse();if(!e.ok){if(e.missing&&this.currentRaw===void 0)return {ok:true,changed:false};for(let r of this.errorListeners)r(e.errors);return {ok:false,errors:e.errors}}if(e.raw===this.currentRaw)return {ok:true,changed:false};this.current=e.value,this.currentRaw=e.raw,this.currentVersion+=1;let n={version:this.currentVersion,names:this.names(),tokenCount:this.current.tokens.length};for(let r of this.changeListeners)r(n);return {ok:true,changed:true}}async readAndParse(){if(!this.filePath)return {ok:false,errors:["Kein Dateipfad konfiguriert."]};let e;try{e=await readFile(this.filePath,"utf8");}catch(s){let i=s.code,o=i==="ENOENT"?"Datei nicht gefunden":s.message;return {ok:false,errors:[`${this.filePath}: ${o}`],missing:i==="ENOENT"}}let n;try{n=JSON.parse(e);}catch(s){return {ok:false,errors:[`${this.filePath}: kein g\xFCltiges JSON (${s instanceof Error?s.message:String(s)})`]}}let r=at(n,this.env);return r.ok?(this.envDefault&&(r.value.destinations.has(se)?this.warnedFileWins||(this.warnedFileWins=true,this.warn(`Sowohl ${this.filePath} als auch die SAP_*-Umgebung definieren die Destination "default" \u2014 die Datei gilt, die Umgebung wird ignoriert.`)):r.value.destinations.set(se,this.envDefault)),{ok:true,value:r.value,raw:e}):r}};var ve=class{buffer=[];maxSize;constructor(e=100){this.maxSize=e;}add(e){this.buffer.length>=this.maxSize&&this.buffer.shift(),this.buffer.push(e);}get(e){return this.buffer.find(n=>n.docnum===e)}list(){return [...this.buffer]}get count(){return this.buffer.length}};var Io=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",parseTagValue:false,processEntities:false,htmlEntities:false,isArray:t=>t==="IDOC"||t.startsWith("E1")||t.startsWith("E2")}),$e=new Set(["@_SEGMENT","@_BEGIN"]);function rn(t,e){return !!(t.startsWith("E1")||t.startsWith("E2")||typeof e=="object"&&e!==null&&"@_SEGMENT"in e)}function ar(t){let e=[];for(let[n,r]of Object.entries(t)){if($e.has(n)||!rn(n,r))continue;let s=Array.isArray(r)?r:[r];for(let i of s){if(typeof i!="object"||i===null)continue;let o={},a=[];for(let[c,l]of Object.entries(i))if(!$e.has(c))if(rn(c,l)){let d=Array.isArray(l)?l:[l];for(let u of d){if(typeof u!="object"||u===null)continue;let m={},p=ar(u);for(let[f,g]of Object.entries(u))$e.has(f)||!rn(f,g)&&typeof g!="object"&&(m[f]=String(g));a.push({name:c,fields:m,...p.length>0?{children:p}:{}});}}else typeof l!="object"&&(o[c]=String(l));e.push({name:n,fields:o,...a.length>0?{children:a}:{}});}}return e}function cr(t){let e=Io.parse(t),n=Object.keys(e).find(u=>u!=="?xml");if(!n)throw new Error("Invalid IDoc XML: no root element found");let r=n,i=e[n].IDOC;if(!i||!Array.isArray(i)||i.length===0)throw new Error("Invalid IDoc XML: no IDOC element found");let o=i[0],a=o.EDI_DC40,c={};if(a){let u=Array.isArray(a)?a[0]:a;for(let[m,p]of Object.entries(u))!$e.has(m)&&typeof p!="object"&&(c[m]=String(p));}let l={};for(let[u,m]of Object.entries(o))u==="EDI_DC40"||$e.has(u)||(l[u]=m);let d=ar(l);return {idocType:r,controlRecord:c,segments:d}}var Do=10*1024*1024,ut=class extends Error{};function No(t,e){return new Promise((n,r)=>{let s=[],i=0,o=false;t.on("data",a=>{if(!o){if(i+=a.length,i>e){o=true,s.length=0,r(new ut(`Request body exceeds ${e} bytes`));return}s.push(a);}}),t.on("end",()=>{o||n(Buffer.concat(s).toString("utf-8"));}),t.on("error",a=>{o||r(a);});})}function xo(t,e){if(e.length===0||typeof t!="string")return false;if(t.startsWith("Bearer "))return Ue(t.slice(7),e);if(t.startsWith("Basic ")){let n=Buffer.from(t.slice(6),"base64").toString("utf-8"),r=n.indexOf(":");return r===-1?false:Ue(n.slice(r+1),e)}return false}function Mo(t){if(typeof t!="string")return true;let e=(t.split(";")[0]??"").trim().toLowerCase();return e.endsWith("xml")||e==="application/x-sap.idoc"}function pt(t,e={}){let n=e.maxBodyBytes??Do;return (r,s)=>{if(r.method!=="POST"){s.writeHead(405,{"Content-Type":"text/plain"}),s.end("Method Not Allowed");return}if(e.secret!==void 0&&!xo(r.headers.authorization,e.secret)){s.writeHead(401,{"Content-Type":"text/plain","WWW-Authenticate":'Basic realm="idoc-webhook"'}),s.end("Unauthorized"),r.destroy();return}if(!Mo(r.headers["content-type"])){s.writeHead(415,{"Content-Type":"text/plain"}),s.end("Unsupported Media Type: expected XML"),r.destroy();return}No(r,n).then(i=>{let{idocType:o,controlRecord:a,segments:c}=cr(i),l={docnum:a.DOCNUM||`UNKNOWN_${Date.now()}`,idocType:o,mesType:a.MESTYP||"",receivedAt:new Date,rawXml:i,parsed:{controlRecord:a,segments:c}};t.add(l),s.writeHead(200,{"Content-Type":"text/plain"}),s.end("OK");}).catch(i=>{if(i instanceof ut){s.writeHead(413,{"Content-Type":"text/plain"}),s.end("Payload Too Large"),r.destroy();return}s.writeHead(400,{"Content-Type":"text/plain"}),s.end(`Parse error: ${i instanceof Error?i.message:String(i)}`);});}}function dr(t,e,n={}){let r=pt(e,n);return new Promise((s,i)=>{let o=createServer((c,l)=>{c.method==="POST"&&c.url==="/idoc"?r(c,l):(l.writeHead(404,{"Content-Type":"text/plain"}),l.end("Not Found"));}),a=()=>{let c=o.address(),l=typeof c=="object"&&c!==null?c.port:t;s({port:l,close:()=>new Promise((d,u)=>{o.close(m=>m?u(m):d());})});};n.host?o.listen(t,n.host,a):o.listen(t,a),o.on("error",i);})}var ur=1;function mt(t){if(!t.directory)return [];let e=join(t.directory,t.fileName),n;try{n=readFileSync(e,"utf-8");}catch{return []}try{let r=JSON.parse(n);if(r?.version!==ur||!Array.isArray(r.entries))return t.onWarn?.(`Cache-Datei ${e} hat ein unbekanntes Format und wird verworfen.`),[];let s=Date.now()-t.ttlMs;return r.entries.filter(i=>i&&typeof i.key=="string"&&typeof i.storedAt=="number"&&i.storedAt>s)}catch(r){return t.onWarn?.(`Cache-Datei ${e} ist unlesbar und wird verworfen. Der Cache wird aus SAP neu aufgebaut.`,r),[]}}function ft(t,e){if(!e.directory)return;let n=join(e.directory,e.fileName),r=`${n}.tmp`;try{mkdirSync(dirname(n),{recursive:!0}),writeFileSync(r,JSON.stringify({version:ur,entries:t}),"utf-8"),renameSync(r,n);}catch(s){e.onWarn?.(`Cache konnte nicht nach ${n} geschrieben werden. Der Betrieb l\xE4uft weiter, nach einem Neustart ist der Cache aber leer.`,s);try{unlinkSync(r);}catch{}}}var Vo=30,Wo="catalog-cache.json",pr="catalog",G=class{cache=null;ttlMs;key;persistence;constructor(e={}){let n=typeof e=="number"?{ttlMinutes:e}:e;this.ttlMs=(n.ttlMinutes??Vo)*60*1e3;let r=(n.baseUrl??"").replace(/\/+$/,"").toLowerCase();if(this.key=r?`${r}|${pr}`:pr,n.persistDir){this.persistence={directory:n.persistDir,fileName:Wo,ttlMs:this.ttlMs,...n.onWarn?{onWarn:n.onWarn}:{}};let s=mt(this.persistence).find(i=>i.key===this.key);s&&(this.cache={entries:s.value,timestamp:s.storedAt});}}get(){return this.cache?Date.now()-this.cache.timestamp>this.ttlMs?(this.cache=null,null):this.cache.entries:null}set(e){this.cache={entries:e,timestamp:Date.now()},this.persist();}clear(){this.cache=null,this.persist();}persist(){this.persistence&&ft(this.cache?[{key:this.key,value:this.cache.entries,storedAt:this.cache.timestamp}]:[],this.persistence);}};var sn=0;function mr(t){let e=t.docnum??`${Date.now().toString().padStart(13,"0")}${String(sn++).padStart(3,"0")}`;sn>=1e3&&(sn=0);let n=t.mesType??t.idocType.replace(/\d+$/,"");return {TABNAM:"EDI_DC40",DOCNUM:e,DIRECT:"2",IDOCTYP:t.idocType,MESTYP:n,SNDPOR:t.sndpor,SNDPRT:t.sndprt??"LS",SNDPRN:t.sndprn,RCVPOR:t.rcvpor,RCVPRT:t.rcvprt??"LS",RCVPRN:t.rcvprn,...t.extra}}function qo(t,e,n){return e.required&&n===""?{segmentName:t,fieldName:e.name,expectedType:e.dataType,expectedLength:e.length,actualValue:n,violation:"Required field is empty"}:n===""?null:(e.dataType==="CHAR"||e.dataType==="NUMC")&&n.length>e.length?{segmentName:t,fieldName:e.name,expectedType:e.dataType,expectedLength:e.length,actualValue:n,violation:`Field value exceeds max length ${e.length} (actual: ${n.length})`}:e.dataType==="NUMC"&&!/^\d*$/.test(n)?{segmentName:t,fieldName:e.name,expectedType:e.dataType,expectedLength:e.length,actualValue:n,violation:"NUMC field must contain only digits"}:e.dataType==="DATS"&&!/^\d{8}$/.test(n)?{segmentName:t,fieldName:e.name,expectedType:e.dataType,expectedLength:e.length,actualValue:n,violation:"DATS field must be in YYYYMMDD format"}:e.dataType==="TIMS"&&!/^\d{6}$/.test(n)?{segmentName:t,fieldName:e.name,expectedType:e.dataType,expectedLength:e.length,actualValue:n,violation:"TIMS field must be in HHMMSS format"}:null}function Ko(t,e){return t.filter(n=>n.name===e).length}function fr(t,e,n){let r=new Set;for(let s of t){if(r.has(s.name))continue;r.add(s.name);let i=e.get(s.name);if(!i)continue;let o=Ko(t,s.name);o>i.maxOccurrence&&n.push({segmentName:s.name,fieldName:"",expectedType:"",expectedLength:i.maxOccurrence,actualValue:String(o),violation:`Segment count ${o} exceeds maxOccurrence ${i.maxOccurrence}`});}for(let s of t){let i=e.get(s.name);if(!i){n.push({segmentName:s.name,fieldName:"",expectedType:"",expectedLength:0,actualValue:"",violation:`Unknown segment '${s.name}' not found in metadata`});continue}for(let o of i.fields){let a=s.fields[o.name]??"",c=qo(s.name,o,a);c&&n.push(c);}s.children&&s.children.length>0&&fr(s.children,e,n);}}function gr(t,e){if(!e)return [];let n=new Map;for(let s of e.segments)n.set(s.name,s);let r=[];return fr(t,n,r),r}var zo=new XMLBuilder({ignoreAttributes:false,attributeNamePrefix:"@_",format:true,suppressEmptyNode:false});function hr(t){let e={"@_SEGMENT":"1"};for(let[n,r]of Object.entries(t.fields))e[n]=r;if(t.children)for(let n of t.children){let r=hr(n),s=e[n.name];s?Array.isArray(s)?s.push(r):e[n.name]=[s,r]:e[n.name]=r;}return e}function yr(t,e,n){let r={};for(let i of n){let o=hr(i),a=r[i.name];a?Array.isArray(a)?a.push(o):r[i.name]=[a,o]:r[i.name]=o;}let s={"?xml":{"@_version":"1.0","@_encoding":"UTF-8"},[t]:{IDOC:{"@_BEGIN":"1",EDI_DC40:{"@_SEGMENT":"1",...e},...r}}};return zo.build(s)}var gt=class{constructor(e,n,r){this.httpClient=e;this.sapClient=n;this.partnerDefaults=r;}async send(e){let n={...this.partnerDefaults,...e.partnerOverrides},r=mr({idocType:e.idocType,sndpor:n.sndpor??"",sndprn:n.sndprn??"",rcvpor:n.rcvpor??"",rcvprn:n.rcvprn??"",sndprt:n.sndprt,rcvprt:n.rcvprt,mesType:n.mesType,docnum:n.docnum,extra:n.extra});if(e.metadata){let u=gr(e.segments,e.metadata);if(u.length>0)return {success:false,docnum:r.DOCNUM,message:"Validation failed",violations:u}}let s=yr(e.idocType,r,e.segments),i="/sap/bc/idoc_xml";this.sapClient&&(i+=`?sap-client=${this.sapClient}`);let o=await this.httpClient.request({method:"post",url:i,data:s,headers:{"Content-Type":"text/xml"}}),a=typeof o.data=="string"?o.data:String(o.data);if(a.includes("<title>IDoc-XML-inbound ok</title>"))return {success:true,docnum:r.DOCNUM,message:"IDoc sent successfully"};let d=a.match(/<title>(.*?)<\/title>/)?.[1]??"IDoc send failed";return {success:false,docnum:r.DOCNUM,message:d}}};var ht=30,Yo=5e3;function Xo(t,e,n,r){let s=t?._meta?.progressToken,i=t?.sendNotification;if(s===void 0||typeof i!="function"||r.afterMs<=0)return ()=>{};let a=setInterval(()=>{let c=Date.now()-n;i({method:"notifications/progress",params:{progressToken:s,progress:Math.round(c/1e3),...r.budgetMs?{total:Math.round(r.budgetMs/1e3)}:{},message:`${e.toolName}: ${Math.round(c/1e3)} s, ${e.sapCalls.count} SAP-Aufruf(e)`}}).catch(()=>{});},r.afterMs);return a.unref?.(),()=>clearInterval(a)}var Jo="SAP_TLS_VERIFY=false: Die Zertifikatspr\xFCfung ist abgeschaltet \u2014 die Verbindung zu SAP ist nicht gegen einen Angreifer in der Leitung gesch\xFCtzt. Richtiger Weg: Root-CA \xFCber SAP_CA_CERT (bzw. NODE_EXTRA_CA_CERTS) einbinden und SAP_TLS_VERIFY wieder entfernen.";function Qo(t){return {...t,content:[...t.content,{type:"text",text:`\u26A0\uFE0F ${Jo}`}]}}function Zo(t,e,n,r,s){return {content:[{type:"text",text:JSON.stringify({truncated:true,reason:`Zeitbudget von ${Math.round(s/1e3)} s \xFCberschritten \u2014 der Aufruf wurde abgebrochen, damit die Verbindung nicht h\xE4ngen bleibt.`,hint:"Grenzen Sie die Anfrage ein (top/skip, filter, search) oder erh\xF6hen Sie --tool-timeout. Der Wert sollte unter der kleinsten Client-Grenze bleiben (Cloudflare 100 s, n8n-MCP-Transport 300 s).",tool:t,correlationId:e,elapsedMs:n,sapCalls:r},null,2)}],isError:true}}function ea(t,e){return {content:[{type:"text",text:JSON.stringify({aborted:true,reason:"Der Client hat die Verbindung verworfen; die Arbeit wurde eingestellt.",tool:t,correlationId:e})}],isError:true}}function Sr(t,e,n={}){let{toolTimeoutMs:r,logger:s,tlsVerifyDisabled:i}=n,o=n.progressAfterMs??Yo;return async(a,c)=>{let l=Pn(),{signal:d,deadline:u}=kn(c?.signal,r),m={correlationId:l,toolName:t,signal:d,deadline:u,sapCalls:{count:0}},p=Date.now();return Rn(m,async()=>{let f=Xo(c,m,p,{afterMs:o,...r?{budgetMs:r}:{}}),g="ok",h,S=(async()=>e(a,c))();S.catch(()=>{});try{h=await Promise.race([S,ta(d,t)]),h?.isError&&(g="error");}catch(y){if(y instanceof $)g=y.timedOut?"timeout":"aborted",h=y.timedOut?Zo(t,l,Date.now()-p,m.sapCalls.count,r??0):ea(t,l);else {g="error";let b=y instanceof Error?y.message:String(y);h={content:[{type:"text",text:JSON.stringify({error:b,tool:t,correlationId:l})}],isError:true};}}g==="ok"&&d?.aborted&&(g=d.reason?.name==="TimeoutError"?"timeout":"aborted"),f();let T=i?Qo(h):h;return s?.info({correlationId:l,tool:t,outcome:g,durationMs:Date.now()-p,sapCalls:m.sapCalls.count,resultBytes:na(h),...r?{budgetMs:r}:{}},"MCP tool call"),T})}}function ta(t,e){return t?new Promise((n,r)=>{let s=()=>{let i=t.reason?.name==="TimeoutError";r(new $(i,e));};if(t.aborted){s();return}t.addEventListener("abort",s,{once:true});}):new Promise(()=>{})}function na(t){if(!t?.content)return 0;let e=0;for(let n of t.content)typeof n?.text=="string"&&(e+=Buffer.byteLength(n.text,"utf-8"));return e}var ra=1440*60*1e3,sa="metadata-cache.json",k=class{cache=new Map;ttlMs;baseUrl;persistence;constructor(e={}){if(this.ttlMs=e.ttlMs??ra,this.baseUrl=ia(e.baseUrl),e.persistDir){this.persistence={directory:e.persistDir,fileName:sa,ttlMs:this.ttlMs,...e.onWarn?{onWarn:e.onWarn}:{}};for(let n of mt(this.persistence))this.cache.set(n.key,{value:n.value,storedAt:n.storedAt});}}get size(){return this.cache.size}get(e){let n=this.key(e),r=this.cache.get(n);if(r){if(Date.now()-r.storedAt>this.ttlMs){this.cache.delete(n);return}return r.value}}set(e,n){this.cache.set(this.key(e),{value:n,storedAt:Date.now()}),this.persist();}has(e){return this.get(e)!==void 0}clear(){this.cache.clear(),this.persist();}key(e){let n=e.replace(/\/+$/,"");return this.baseUrl?`${this.baseUrl}|${n}`:n}persist(){this.persistence&&ft([...this.cache.entries()].map(([e,n])=>({key:e,value:n.value,storedAt:n.storedAt})),this.persistence);}};function ia(t){return t?t.replace(/\/+$/,"").toLowerCase():""}var Tr="0.0.0-unknown",Ar="guniweb-sap-mcp";function la(t){if(typeof t!="string"||t.length===0)return Tr;let e=resolve(t);for(;;){try{let r=readFileSync(join(e,"package.json"),"utf-8"),s=JSON.parse(r);if(s.name===Ar&&typeof s.version=="string")return s.version}catch{}let n=dirname(e);if(n===e)return Tr;e=n;}}var br=la(import.meta.dirname),vr=Ar;function wr(t){t.registerPrompt("nl-to-odata-query",{title:"Natural Language to OData Query",description:"Convert a natural language question into a correct OData query against an SAP service. Reads entity schema for context, applies V2/V4 syntax rules, and enforces read-only by default.",argsSchema:{serviceUrl:z$2.string().describe("OData service URL, e.g., /sap/opu/odata/sap/API_BUSINESS_PARTNER"),entitySet:z$2.string().describe("Entity set to query, e.g., A_BusinessPartner"),question:z$2.string().describe('Natural language question, e.g., "show me all customers in Germany"'),allowWrite:z$2.boolean().optional().describe("Allow write operations (create/update/delete). Default: false (read-only)")}},async({serviceUrl:e,entitySet:n,question:r,allowWrite:s})=>{let i=s===true?"":`
|
|
110
110
|
|
|
111
111
|
IMPORTANT \u2014 READ-ONLY MODE:
|
|
112
112
|
You MUST only generate read queries (sap_query or sap_nl_query). Do NOT use sap_create, sap_update, or sap_delete.
|
|
@@ -200,7 +200,7 @@ ${s}`}}]}});}var Tt=class{cache=new Map;buildKey(e,n){let s=Object.entries(n).so
|
|
|
200
200
|
`),a;o!==-1&&(a=i.substring(o+2).trim());let c={status:n,entitySet:""};if(a&&a.length>0)if(r)try{c.result=JSON.parse(a);}catch{}else c.error=a;return c}function Cr(t,e){let n=[],r=t.split(`--${e}`);for(let s of r){let i=s.trim();if(!i||i==="--")continue;let o=i.match(/Content-Type:\s*multipart\/mixed;\s*boundary=([^\s\r\n]+)/i);if(o){let c=o[1],l=i.indexOf(`
|
|
201
201
|
|
|
202
202
|
`);if(l!==-1){let d=i.substring(l+2),u=Cr(d,c);n.push(...u);}continue}let a=ua(i);a&&n.push(a);}return n}function Pr(t,e){let n=e.match(/boundary=([^\s;]+)/);if(!n)return [];let r=n[1],s=t.replace(/\r\n/g,`
|
|
203
|
-
`);return Cr(s,r)}var pa=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function Rr(t){return typeof t=="number"?String(t):typeof t=="string"&&pa.test(t)?`guid'${t}'`:`'${String(t)}'`}function
|
|
203
|
+
`);return Cr(s,r)}var pa=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function Rr(t){return typeof t=="number"?String(t):typeof t=="string"&&pa.test(t)?`guid'${t}'`:`'${String(t)}'`}function we(t,e){let n=Object.entries(e);if(n.length===1){let s=n[0];return `${t}(${Rr(s[1])})`}let r=n.map(([s,i])=>`${s}=${Rr(i)}`);return `${t}(${r.join(",")})`}function on(t,e){let n={$format:"json"};return t.filter&&(n.$filter=t.filter),t.select&&t.select.length>0&&(n.$select=t.select.join(",")),t.expand&&t.expand.length>0&&(n.$expand=t.expand.join(",")),t.orderby&&(n.$orderby=t.orderby),t.top!==void 0&&(n.$top=String(t.top)),t.skip!==void 0&&(n.$skip=String(t.skip)),e&&(n.$inlinecount="allpages"),n}function ma(t){return t.key?we(t.entitySet,t.key):t.entitySet}function kr(t){let e=ma(t),n=t.method.toUpperCase(),r=n==="PATCH",s=r?"POST":n,i=[];if(i.push("Content-Type: application/http"),i.push("Content-Transfer-Encoding: binary"),i.push(""),i.push(`${s} ${e} HTTP/1.1`),n==="GET"?i.push("Accept: application/json"):(i.push("Content-Type: application/json"),i.push("Accept: application/json"),r&&i.push("X-HTTP-Method: MERGE"),t.data&&"__etag"in t.data&&i.push(`If-Match: ${t.data.__etag}`)),i.push(""),t.data&&(n==="POST"||n==="PATCH"||n==="DELETE")){let{__etag:o,...a}=t.data;Object.keys(a).length>0&&i.push(JSON.stringify(a));}return i.join(`\r
|
|
204
204
|
`)}function Or(t,e,n){let r=`batch_${randomUUID()}`,s=[];for(let a of e)s.push(`--${r}\r
|
|
205
205
|
${kr(a)}`);if(n)for(let a of n){let c=`changeset_${randomUUID()}`,l=[];for(let u of a.operations)l.push(`--${c}\r
|
|
206
206
|
${kr(u)}`);l.push(`--${c}--`);let d=l.join(`\r
|
|
@@ -208,7 +208,7 @@ ${kr(u)}`);l.push(`--${c}--`);let d=l.join(`\r
|
|
|
208
208
|
Content-Type: multipart/mixed; boundary=${c}\r
|
|
209
209
|
\r
|
|
210
210
|
${d}`);}s.push(`--${r}--`);let i=s.join(`\r
|
|
211
|
-
`),o=`multipart/mixed; boundary=${r}`;return {body:i,contentType:o}}var ga=new Set(["EntityType","Property","NavigationProperty","Association","EntitySet","AssociationSet","PropertyRef","End","FunctionImport","Parameter"]),ha=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",removeNSPrefix:true,processEntities:false,htmlEntities:false,isArray:t=>ga.has(t)});function an(t){return t===void 0?true:String(t).toLowerCase()!=="false"}function At(t,e){let r=ha.parse(t).Edmx.DataServices.Schema,s=Array.isArray(r)?r[0]:r,i=s.EntityType??[],o=s.Association??[],a=s.EntityContainer,c=a?.EntitySet??[],l=new Map;for(let g of o)l.set(g["@_Name"],g);let d=i.map(g=>{let h=ya(g.Property??[]),S=(g.Key?.PropertyRef??[]).map(b=>b["@_Name"]),T=h.filter(b=>S.includes(b.name)),y=Sa(g.NavigationProperty??[],l);return {name:g["@_Name"],keys:T,properties:h,navigationProperties:y}}),u=c.map(g=>({name:g["@_Name"],entityTypeName:cn(g["@_EntityType"]),creatable:an(g["@_creatable"]),updatable:an(g["@_updatable"]),deletable:an(g["@_deletable"])})),m=a?.FunctionImport??[],p=Ta(Array.isArray(m)?m:[m]),f={version:"v2",serviceUrl:e,entityTypes:d,entitySets:u};return p.length>0&&(f.functionImports=p),f}function ya(t){return t.map(e=>{let n={name:e["@_Name"],type:e["@_Type"],nullable:e["@_Nullable"]!=="false"};return e["@_MaxLength"]!==void 0&&(n.maxLength=Number.parseInt(e["@_MaxLength"],10)),e["@_Precision"]!==void 0&&(n.precision=Number.parseInt(e["@_Precision"],10)),e["@_Scale"]!==void 0&&(n.scale=Number.parseInt(e["@_Scale"],10)),n})}function Sa(t,e){return t.map(n=>{let r=cn(n["@_Relationship"]),s=e.get(r),i="Unknown",o=false;if(s){let a=s.End.find(c=>c["@_Role"]===n["@_ToRole"]);a&&(i=cn(a["@_Type"]),o=a["@_Multiplicity"]==="*");}return {name:n["@_Name"],targetEntityType:i,isCollection:o}})}function Ta(t){return t.filter(e=>e["@_Name"]).map(e=>{let n=(e.Parameter??[]).map(s=>({name:s["@_Name"],type:s["@_Type"],...s["@_Nullable"]==="true"?{nullable:true}:{}})),r={name:e["@_Name"],httpMethod:e["@_HttpMethod"]??"POST",parameters:n};return e["@_ReturnType"]&&(r.returnType=e["@_ReturnType"]),r})}function cn(t){let e=t.lastIndexOf(".");return e>=0?t.substring(e+1):t}var Aa=/^\/Date\((-?\d+)([+-]\d{4})?\)\/$/;function Le(t){if(!t||typeof t!="object")return {results:[]};let e=t;if("d"in e&&e.d&&typeof e.d=="object"){let n=e.d;if("results"in n&&Array.isArray(n.results)){let s={results:n.results};return n.__count!==void 0&&(s.count=Number(n.__count)),typeof n.__next=="string"&&(s.nextLink=n.__next),s}return {results:[{...n}]}}return "results"in e&&Array.isArray(e.results)?{results:e.results}:{results:[e]}}function Ir(t){let e=Aa.exec(t);if(!e)return t;let n=Number(e[1]);return new Date(n).toISOString()}function ie(t){if(t==null)return t;if(Array.isArray(t))return t.map(e=>ie(e));if(typeof t=="object"){let e=t,n={};for(let[r,s]of Object.entries(e))r!=="__metadata"&&(s&&typeof s=="object"&&!Array.isArray(s)&&"__deferred"in s||(typeof s=="string"?n[r]=Ir(s):n[r]=ie(s)));return n}return typeof t=="string"?Ir(t):t}var Dr=10,Be=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;}async read(e,n,r){let s=on(r??{});if(n){let i=`${this.serviceUrl}/${ve(e,n)}`,o=await this.httpClient.request({method:"get",url:i,headers:{Accept:"application/json"},params:s});o.headers.etag&&this.etagCache.set(e,n,o.headers.etag);let a=Le(o.data);return {...a,results:a.results.map(c=>ie(c))}}return this.fetchWithPagination(`${this.serviceUrl}/${e}`,s,r?.maxPages??Dr)}async query(e,n){let r=on(n,true),s=n.maxPages??Dr;return this.fetchWithPagination(`${this.serviceUrl}/${e}`,r,s)}async create(e,n){let r=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/${e}`,data:n,headers:{Accept:"application/json","Content-Type":"application/json"}}),s=Le(r.data);return s.results.length>0?ie(s.results[0]):{}}async update(e,n,r){let s=`${this.serviceUrl}/${ve(e,n)}`,i=this.etagCache.get(e,n)??"*",o=await this.httpClient.request({method:"post",url:s,data:r,headers:{Accept:"application/json","Content-Type":"application/json","X-HTTP-Method":"MERGE","If-Match":i}});o.headers.etag&&this.etagCache.set(e,n,o.headers.etag);}async delete(e,n){let r=`${this.serviceUrl}/${ve(e,n)}`,s=this.etagCache.get(e,n)??"*";await this.httpClient.request({method:"delete",url:r,headers:{Accept:"application/json","If-Match":s}}),this.etagCache.update(e,n,void 0);}async functionImport(e,n,r){let s=(r??"POST").toLowerCase(),i={$format:"json"};if(n)for(let[l,d]of Object.entries(n))typeof d=="string"?i[l]=`'${d}'`:i[l]=String(d);let o=await this.httpClient.request({method:s,url:`${this.serviceUrl}/${e}`,headers:{Accept:"application/json"},params:i}),a=Le(o.data);if(a.results.length>0)return ie(a.results[0]);let c=o.data;return c?.d&&typeof c.d=="object"?c.d:{}}async batch(e,n){let{body:r,contentType:s}=Or(this.serviceUrl,e,n),i=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:r,headers:{"Content-Type":s,Accept:"multipart/mixed"}}),o=i.headers["content-type"]??s,a=Pr(i.data,o),c=[...e,...(n??[]).flatMap(l=>l.operations)];for(let l=0;l<a.length&&l<c.length;l++)a[l].entitySet=c[l].entitySet;return {results:a}}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml"}});return At(e.data,this.serviceUrl)}async fetchWithPagination(e,n,r){let s=[],i,o=e,a=n,c=0,l=false;for(;o&&c<r;){if(c>0&&Je()){l=true;break}let m=(await this.httpClient.request({method:"get",url:o,headers:{Accept:"application/json"},params:a})).data,p=Le(m),f=p.results.map(h=>ie(h));s.push(...f),c===0&&p.count!==void 0&&(i=p.count),c++;let g=m?.d;g&&typeof g.__next=="string"?(o=g.__next,a=void 0):o=void 0;}let d={results:s};return i!==void 0&&(d.count=i),o&&(d.nextLink=o),l&&(d.truncated=true,d.truncationReason=`Nach ${c} Seite(n) abgebrochen: Zeitbudget des Tool-Aufrufs ersch\xF6pft oder Client hat die Verbindung verworfen. Mit skip=${s.length} weiterlesen.`),d}};function Nr(t){let n=(typeof t=="string"?JSON.parse(t):t).responses;return !n||!Array.isArray(n)?[]:n.map(r=>{let s=r.status,i=s>=200&&s<300,o={status:s,entitySet:""};return r.body!==void 0&&r.body!==null&&(i?o.result=r.body:o.error=JSON.stringify(r.body)),o})}function xr(t){return typeof t=="string"?`'${t}'`:String(t)}function z(t,e){let n=Object.entries(e);if(n.length===1){let[,s]=n[0];return `${t}(${xr(s)})`}let r=n.map(([s,i])=>`${s}=${xr(i)}`).join(",");return `${t}(${r})`}function bt(t,e){let n={};return t.filter&&(n.$filter=t.filter),t.select?.length&&(n.$select=t.select.join(",")),t.expand?.length&&(n.$expand=t.expand.join(",")),t.orderby&&(n.$orderby=t.orderby),t.top!==void 0&&(n.$top=String(t.top)),t.skip!==void 0&&(n.$skip=String(t.skip)),e&&(n.$count="true"),n}function Mr(t,e){let n=[];for(let s=0;s<e.length;s++){let i=e[s],o=i.key?z(i.entitySet,i.key):i.entitySet,a={id:String(s+1),method:i.method.toUpperCase(),url:o};i.changesetId&&(a.atomicityGroup=i.changesetId),i.data&&(i.method==="post"||i.method==="patch"||i.method==="delete")&&(a.headers={"Content-Type":"application/json"},a.body=i.data),n.push(a);}return {body:JSON.stringify({requests:n}),contentType:"application/json"}}function Ur(t){let e=t.split(".");return e[e.length-1]}function ba(t,e){return {name:t,type:e.$Type??"Edm.String",nullable:e.$Nullable??false,maxLength:e.$MaxLength,precision:e.$Precision,scale:e.$Scale}}function $r(t,e){let n=JSON.parse(t),r=[],s=[],i=[];for(let[a,c]of Object.entries(n)){if(a.startsWith("$"))continue;let l=c;for(let[d,u]of Object.entries(l)){let m=u;if(m.$Kind==="Action"||m.$Kind==="Function"){let p=m.$IsBound??false,f=m.$Parameter??[],g=p&&f.length>0?f[0].$Name:void 0,h=(p?f.slice(1):f).map(T=>({name:T.$Name,type:T.$Type??"Edm.String",...T.$Nullable===true?{nullable:true}:{}})),S=m.$ReturnType?m.$ReturnType.$Type:void 0;i.push({name:d,isBound:p,...g?{bindingParameter:g}:{},parameters:h,...S?{returnType:S}:{}});}else if(m.$Kind==="EntityType"){let p=[],f=[];for(let[S,T]of Object.entries(m)){if(S.startsWith("$"))continue;let y=T;y.$Kind==="NavigationProperty"?f.push({name:S,targetEntityType:Ur(y.$Type),isCollection:y.$Collection??false}):p.push(ba(S,y));}let h=(m.$Key??[]).map(S=>{let T=p.find(y=>y.name===S);return T||{name:S,type:"Edm.String",nullable:false}});r.push({name:d,keys:h,properties:p,navigationProperties:f});}else if(m.$Kind==="EntityContainer")for(let[p,f]of Object.entries(m)){if(p.startsWith("$"))continue;let g=f;g.$Collection&&g.$Type&&s.push({name:p,entityTypeName:Ur(g.$Type),creatable:dn(g,"InsertRestrictions","Insertable"),updatable:dn(g,"UpdateRestrictions","Updatable"),deletable:dn(g,"DeleteRestrictions","Deletable")});}}}let o={version:"v4",serviceUrl:e,entityTypes:r,entitySets:s};return i.length>0&&(o.actions=i),o}function dn(t,e,n){for(let[r,s]of Object.entries(t)){if(!r.startsWith("@")||!r.endsWith(e))continue;let i=s?.[n];if(typeof i=="boolean")return i}return true}var wa=10,wt=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;}async read(e,n,r){if(n){let l=`${this.serviceUrl}/${z(e,n)}`,d=r?bt(r):void 0,u=await this.httpClient.request({method:"get",url:l,params:d}),m=u.data,p=u.headers.etag??u.headers.ETag;return p&&this.etagCache.set(e,n,p),{results:[m]}}let s=r?bt(r):void 0,o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=o.value??[],c=o["@odata.count"];return {results:a,count:c}}async query(e,n){let r=n.maxPages??wa,s=bt(n,true),o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=[...o.value??[]],c=o["@odata.count"],l=o["@odata.nextLink"],d=1,u=false;for(;l&&d<r;){if(Je()){u=true;break}let p=this.resolveNextLink(l),g=(await this.httpClient.request({method:"get",url:p})).data,h=g.value??[];a.push(...h),l=g["@odata.nextLink"],d++;}let m={results:a,count:c};return l&&(m.nextLink=l),u&&(m.truncated=true,m.truncationReason=`Nach ${d} Seite(n) abgebrochen: Zeitbudget des Tool-Aufrufs ersch\xF6pft oder Client hat die Verbindung verworfen. Mit skip=${a.length} weiterlesen.`),m}async create(e,n){return (await this.httpClient.request({method:"post",url:`${this.serviceUrl}/${e}`,data:n,headers:{"Content-Type":"application/json"}})).data}async update(e,n,r){let s=`${this.serviceUrl}/${z(e,n)}`,i=this.etagCache.get(e,n)??"*",o=await this.httpClient.request({method:"patch",url:s,data:r,headers:{"Content-Type":"application/json","If-Match":i}}),a=o.headers.etag??o.headers.ETag;a&&this.etagCache.update(e,n,a);}async delete(e,n){let r=`${this.serviceUrl}/${z(e,n)}`,s=this.etagCache.get(e,n)??"*";await this.httpClient.request({method:"delete",url:r,headers:{"If-Match":s}});}async batch(e){let{body:n,contentType:r}=Mr(this.serviceUrl,e),s=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:n,headers:{"Content-Type":r,Accept:"application/json"}}),i=Nr(s.data);for(let o=0;o<i.length&&o<e.length;o++)i[o].entitySet=e[o].entitySet;return {results:i}}async callAction(e,n,r,s){let i;return r&&s?i=`${this.serviceUrl}/${z(r,s)}/${e}`:i=`${this.serviceUrl}/${e}`,(await this.httpClient.request({method:"post",url:i,data:n??{},headers:{"Content-Type":"application/json"}})).data??{}}async callFunction(e,n,r,s){let i=this.buildFunctionParams(n),o;return r&&s?o=`${this.serviceUrl}/${z(r,s)}/${e}(${i})`:o=`${this.serviceUrl}/${e}(${i})`,(await this.httpClient.request({method:"get",url:o})).data??{}}buildFunctionParams(e){return !e||Object.keys(e).length===0?"":Object.entries(e).map(([n,r])=>typeof r=="string"?`${n}='${r}'`:`${n}=${String(r)}`).join(",")}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/json"}}),n=typeof e.data=="string"?e.data:JSON.stringify(e.data);return $r(n,this.serviceUrl)}resolveNextLink(e){return e.startsWith("http://")||e.startsWith("https://")?e:`${this.serviceUrl}/${e}`}};function ln(t,e){let n=t.trim();if(n.startsWith("{"))try{if("$Version"in JSON.parse(n))return "v4"}catch{}if(n.startsWith("<?xml")||n.startsWith("<edmx:Edmx")||n.startsWith("<Edmx"))return "v2";if(e){if(e.includes("/odata4/"))return "v4";if(e.includes("/odata/"))return "v2"}return "v2"}var M=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.metadataCache=r??new k;}protocolClient=null;version=null;initPromise=null;etagCache=new Tt;metadataCache;async ensureInitialized(){if(!this.protocolClient)return this.initPromise?this.initPromise:(this.initPromise=this.initialize(),this.initPromise)}async initialize(){let e=this.metadataCache.get(this.serviceUrl);if(e){this.version=e.version,this.protocolClient=this.createProtocolClient(e.version);return}let n="",r=false;try{let s=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml, application/json"}});n=typeof s.data=="string"?s.data:JSON.stringify(s.data),this.version=ln(n,this.serviceUrl),r=!0;}catch{this.version=ln("",this.serviceUrl);}if(this.protocolClient=this.createProtocolClient(this.version),r&&this.version==="v2"&&n)try{let s=At(n,this.serviceUrl);this.metadataCache.set(this.serviceUrl,s);}catch{}}createProtocolClient(e){return e==="v4"?new wt(this.httpClient,this.serviceUrl,this.etagCache):new Be(this.httpClient,this.serviceUrl,this.etagCache)}async getClient(){if(await this.ensureInitialized(),!this.protocolClient)throw new Error("ODataClient initialization failed: protocol client not created");return this.protocolClient}async read(e,n,r){return (await this.getClient()).read(e,n,r)}async query(e,n){return (await this.getClient()).query(e,n)}async create(e,n){return (await this.getClient()).create(e,n)}async update(e,n,r){return (await this.getClient()).update(e,n,r)}async delete(e,n){return (await this.getClient()).delete(e,n)}async batch(e,n){let r=await this.getClient();return this.version==="v4"?r.batch(e):r.batch(e,n)}async functionImport(e,n,r){let s=await this.getClient();if(s instanceof Be)return s.functionImport(e,n,r?.httpMethod);let i=s;return r?.isFunction?i.callFunction(e,n,r?.entitySet,r?.key):i.callAction(e,n,r?.entitySet,r?.key)}async getMetadata(){let e=this.metadataCache.get(this.serviceUrl);if(e)return e;let n=await this.getClient();if(e=this.metadataCache.get(this.serviceUrl),e)return e;let r=await n.getMetadata();return this.metadataCache.set(this.serviceUrl,r),r}getVersion(){return this.version}};function Lr(t,e,n){let r=n??new k,s=new ResourceTemplate("sap://schema/{serviceUrl}/{entitySet}",{list:void 0});t.registerResource("sap-entity-schema",s,{title:"SAP Entity Schema for Query Building",description:"Property names, types, and keys for an entity set \u2014 optimized context for building OData queries. Includes a sample filter in the correct V2/V4 syntax.",mimeType:"application/json"},async(i,o)=>{let a=decodeURIComponent(o.serviceUrl),c=decodeURIComponent(o.entitySet),d=await new M(e,a,r).getMetadata(),u=d.entitySets.find(h=>h.name===c);if(!u)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity set '${c}' not found in service ${a}`,availableEntitySets:d.entitySets.map(h=>h.name)},null,2)}]};let m=d.entityTypes.find(h=>h.name===u.entityTypeName);if(!m)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity type '${u.entityTypeName}' not found in metadata`},null,2)}]};let p=new Set(m.keys.map(h=>h.name)),f=m.properties.map(h=>({name:h.name,type:h.type,isKey:p.has(h.name)})),g=Ea(m.properties,d.version);return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({entitySet:c,version:d.version,properties:f,sampleFilter:g},null,2)}]}});}function Ea(t,e){let n=t.find(r=>r.type==="Edm.String");return n?e==="v2"?`${n.name} eq 'example' or substringof('example',${n.name})`:`${n.name} eq 'example' or contains(${n.name},'example')`:"PropertyName eq 'value'"}function Br(t,e){let n=new ResourceTemplate("idoc://received/{docnum}",{list:void 0});t.registerResource("idoc-received",n,{title:"Received IDoc",description:"Received IDoc data from webhook",mimeType:"application/json"},async(r,s)=>{let i=s.docnum,o=e.get(i);return o?{contents:[{uri:r.toString(),mimeType:"application/json",text:JSON.stringify({docnum:o.docnum,idocType:o.idocType,mesType:o.mesType,receivedAt:o.receivedAt.toISOString(),parsed:o.parsed},null,2)}]}:{contents:[{uri:r.toString(),mimeType:"application/json",text:JSON.stringify({error:`IDoc with docnum '${i}' not found`},null,2)}]}});}function Fr(t,e,n){let r=n??new k,s=new ResourceTemplate("sap://metadata/{serviceUrl}",{list:void 0});t.registerResource("sap-metadata",s,{title:"SAP Service Metadata",description:"Entity types, properties, keys, and navigation properties for an SAP OData service",mimeType:"application/json"},async(i,o)=>{let a=decodeURIComponent(o.serviceUrl),l=await new M(e,a,r).getMetadata();return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({version:l.version,serviceUrl:l.serviceUrl,entityTypes:l.entityTypes,entitySets:l.entitySets},null,2)}]}});}var un={"business-partner":{domain:"business-partner",description:"Customer, supplier, and business partner master data",services:[{name:"API_BUSINESS_PARTNER",path:"/sap/opu/odata/sap/API_BUSINESS_PARTNER",version:"v2",keyEntitySets:["A_BusinessPartner","A_BusinessPartnerAddress","A_Customer","A_Supplier"]}],queryTemplates:[{name:"find-customer-by-name",description:"Find customers by name (substring search)",entitySet:"A_Customer",filters:[{field:"CustomerName",operator:"contains",value:""}],select:["Customer","CustomerName","CustomerFullName","Country"],top:20},{name:"find-supplier-by-country",description:"Find suppliers in a specific country",entitySet:"A_Supplier",filters:[{field:"Country",operator:"eq",value:""}],select:["Supplier","SupplierName","Country","Region"],top:50}],suggestedTool:"sap_nl_query",commonQueries:["Find customers by name","List suppliers in a country","Get business partner addresses"]},sales:{domain:"sales",description:"Sales orders, billing documents, and revenue tracking",services:[{name:"API_SALES_ORDER_SRV",path:"/sap/opu/odata/sap/API_SALES_ORDER_SRV",version:"v2",keyEntitySets:["A_SalesOrder","A_SalesOrderItem"]},{name:"API_BILLING_DOCUMENT_SRV",path:"/sap/opu/odata/sap/API_BILLING_DOCUMENT_SRV",version:"v2",keyEntitySets:["A_BillingDocument"]}],queryTemplates:[{name:"recent-sales-orders",description:"Sales orders created this month",entitySet:"A_SalesOrder",filters:[{field:"CreationDate",operator:"ge",value:"$MONTH_START"}],select:["SalesOrder","SalesOrderType","SoldToParty","TotalNetAmount","CreationDate"],orderby:"CreationDate desc",top:50},{name:"overdue-invoices",description:"Billing documents past due date",entitySet:"A_BillingDocument",filters:[{field:"BillingDocumentDate",operator:"lt",value:"$TODAY"}],select:["BillingDocument","BillingDocumentType","SoldToParty","TotalNetAmount","BillingDocumentDate"],orderby:"BillingDocumentDate asc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Recent sales orders","Overdue invoices","Sales by customer"]},finance:{domain:"finance",description:"Journal entries, accounting documents, and financial postings",services:[{name:"API_JOURNALENTRY_SRV",path:"/sap/opu/odata/sap/API_JOURNALENTRY_SRV",version:"v2",keyEntitySets:["A_JournalEntry"]},{name:"API_OPLACCTG_SRV",path:"/sap/opu/odata/sap/API_OPLACCTG_SRV",version:"v2",keyEntitySets:["A_OperationalAcctgDocItem"]}],queryTemplates:[{name:"recent-journal-entries",description:"Journal entries posted this month",entitySet:"A_JournalEntry",filters:[{field:"DocumentDate",operator:"ge",value:"$MONTH_START"}],select:["AccountingDocument","CompanyCode","DocumentDate","PostingDate","DocumentType"],orderby:"DocumentDate desc",top:50},{name:"open-items-by-account",description:"Open accounting items for a G/L account",entitySet:"A_OperationalAcctgDocItem",filters:[{field:"GLAccount",operator:"eq",value:""}],select:["AccountingDocument","CompanyCode","GLAccount","AmountInCompanyCodeCurrency","PostingDate"],top:100}],suggestedTool:"sap_nl_query",commonQueries:["Recent journal entries","Open items by account","Accounting documents this month"]},procurement:{domain:"procurement",description:"Purchase orders, purchase requisitions, and supplier management",services:[{name:"API_PURCHASEORDER_PROCESS_SRV",path:"/sap/opu/odata/sap/API_PURCHASEORDER_PROCESS_SRV",version:"v2",keyEntitySets:["A_PurchaseOrder","A_PurchaseOrderItem"]},{name:"API_PURCHASEREQ_PROCESS_SRV",path:"/sap/opu/odata/sap/API_PURCHASEREQ_PROCESS_SRV",version:"v2",keyEntitySets:["A_PurchaseRequisition"]}],queryTemplates:[{name:"open-purchase-orders",description:"Purchase orders not yet fully delivered",entitySet:"A_PurchaseOrder",filters:[],select:["PurchaseOrder","PurchaseOrderType","Supplier","PurchasingOrganization","CreationDate"],orderby:"CreationDate desc",top:50},{name:"recent-purchase-requisitions",description:"Purchase requisitions created this month",entitySet:"A_PurchaseRequisition",filters:[{field:"CreationDate",operator:"ge",value:"$MONTH_START"}],select:["PurchaseRequisition","PurchaseRequisitionType","CreationDate"],orderby:"CreationDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Open purchase orders","Recent purchase requisitions","Purchase orders by supplier"]},material:{domain:"material",description:"Material master data and material document movements",services:[{name:"API_PRODUCT_SRV",path:"/sap/opu/odata/sap/API_PRODUCT_SRV",version:"v2",keyEntitySets:["A_Product"]},{name:"API_MATERIAL_DOCUMENT_SRV",path:"/sap/opu/odata/sap/API_MATERIAL_DOCUMENT_SRV",version:"v2",keyEntitySets:["A_MaterialDocumentHeader"]}],queryTemplates:[{name:"find-material-by-description",description:"Search materials by description text",entitySet:"A_Product",filters:[{field:"ProductDescription",operator:"contains",value:""}],select:["Product","ProductType","ProductDescription","BaseUnit"],top:20},{name:"recent-material-movements",description:"Material documents created this month",entitySet:"A_MaterialDocumentHeader",filters:[{field:"DocumentDate",operator:"ge",value:"$MONTH_START"}],select:["MaterialDocument","MaterialDocumentYear","DocumentDate","PostingDate"],orderby:"DocumentDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Find material by description","Recent material movements","Material stock levels"]},logistics:{domain:"logistics",description:"Inbound and outbound deliveries, shipping, and transportation",services:[{name:"API_INBOUND_DELIVERY_SRV",path:"/sap/opu/odata/sap/API_INBOUND_DELIVERY_SRV",version:"v2",keyEntitySets:["A_InbDeliveryHeader"]},{name:"API_OUTBOUND_DELIVERY_SRV",path:"/sap/opu/odata/sap/API_OUTBOUND_DELIVERY_SRV",version:"v2",keyEntitySets:["A_OutbDeliveryHeader"]}],queryTemplates:[{name:"pending-inbound-deliveries",description:"Inbound deliveries not yet received",entitySet:"A_InbDeliveryHeader",filters:[],select:["DeliveryDocument","Supplier","DeliveryDate","ReceivingPlant"],orderby:"DeliveryDate asc",top:50},{name:"recent-outbound-deliveries",description:"Outbound deliveries created this month",entitySet:"A_OutbDeliveryHeader",filters:[{field:"CreationDate",operator:"ge",value:"$MONTH_START"}],select:["DeliveryDocument","SoldToParty","CreationDate","DeliveryDate"],orderby:"CreationDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Pending inbound deliveries","Recent outbound deliveries","Deliveries by ship-to party"]},hr:{domain:"hr",description:"Employee master data and organizational assignments",services:[{name:"API_BUSINESS_PARTNER",path:"/sap/opu/odata/sap/API_BUSINESS_PARTNER",version:"v2",keyEntitySets:["A_BusinessPartner"]}],queryTemplates:[{name:"find-employee-by-name",description:"Search employees by name (BP category 2 = Person)",entitySet:"A_BusinessPartner",filters:[{field:"BusinessPartnerCategory",operator:"eq",value:"2"},{field:"BusinessPartnerFullName",operator:"contains",value:""}],select:["BusinessPartner","BusinessPartnerFullName","BusinessPartnerCategory","OrganizationBPName1"],top:20},{name:"employees-by-department",description:"List employees assigned to a department/organization",entitySet:"A_BusinessPartner",filters:[{field:"BusinessPartnerCategory",operator:"eq",value:"2"},{field:"OrganizationBPName1",operator:"contains",value:""}],select:["BusinessPartner","BusinessPartnerFullName","OrganizationBPName1"],top:50}],suggestedTool:"sap_nl_query",commonQueries:["Find employee by name","Employees by department"]},production:{domain:"production",description:"Production orders, planned orders, and manufacturing execution",services:[{name:"API_PLANNED_ORDER",path:"/sap/opu/odata/sap/API_PLANNED_ORDER",version:"v2",keyEntitySets:["PlannedOrder"]},{name:"API_PRODUCTION_ORDER",path:"/sap/opu/odata/sap/API_PRODUCTION_ORDER",version:"v2",keyEntitySets:["A_ProductionOrder"]}],queryTemplates:[{name:"open-production-orders",description:"Production orders not yet completed",entitySet:"A_ProductionOrder",filters:[],select:["ManufacturingOrder","ManufacturingOrderType","Material","ProductionPlant","MfgOrderPlannedStartDate"],orderby:"MfgOrderPlannedStartDate asc",top:50},{name:"planned-orders-this-month",description:"Planned orders created this month",entitySet:"PlannedOrder",filters:[{field:"PlannedOrderCreationDate",operator:"ge",value:"$MONTH_START"}],select:["PlannedOrder","Material","ProductionPlant","PlannedOrderCreationDate"],orderby:"PlannedOrderCreationDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Open production orders","Planned orders this month","Production orders by material"]},quality:{domain:"quality",description:"Quality inspections, inspection lots, and quality management",services:[{name:"API_QUALITYINSPECTION",path:"/sap/opu/odata/sap/API_QUALITYINSPECTION",version:"v2",keyEntitySets:["QualityInspection"]}],queryTemplates:[{name:"open-inspections",description:"Quality inspections not yet completed",entitySet:"QualityInspection",filters:[],select:["InspectionLot","Material","Plant","InspectionLotStartDate"],orderby:"InspectionLotStartDate asc",top:50},{name:"inspections-this-month",description:"Quality inspections started this month",entitySet:"QualityInspection",filters:[{field:"InspectionLotStartDate",operator:"ge",value:"$MONTH_START"}],select:["InspectionLot","Material","Plant","InspectionLotStartDate"],orderby:"InspectionLotStartDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Open quality inspections","Inspections this month"]},"plant-maintenance":{domain:"plant-maintenance",description:"Maintenance notifications, orders, and equipment management",services:[{name:"API_MAINTNOTIFICATION",path:"/sap/opu/odata/sap/API_MAINTNOTIFICATION",version:"v2",keyEntitySets:["MaintenanceNotification"]},{name:"API_MAINTENANCEORDER",path:"/sap/opu/odata/sap/API_MAINTENANCEORDER",version:"v2",keyEntitySets:["MaintenanceOrder"]}],queryTemplates:[{name:"open-maintenance-notifications",description:"Maintenance notifications not yet completed",entitySet:"MaintenanceNotification",filters:[],select:["MaintenanceNotification","NotificationType","FunctionalLocation","ReportedByUser","CreationDate"],orderby:"CreationDate desc",top:50},{name:"maintenance-orders-this-month",description:"Maintenance orders created this month",entitySet:"MaintenanceOrder",filters:[{field:"CreationDate",operator:"ge",value:"$MONTH_START"}],select:["MaintenanceOrder","MaintenanceOrderType","FunctionalLocation","CreationDate"],orderby:"CreationDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Open maintenance notifications","Maintenance orders this month","Notifications by functional location"]},project:{domain:"project",description:"Project systems, WBS elements, and project planning",services:[{name:"API_PROJECT",path:"/sap/opu/odata/sap/API_PROJECT",version:"v2",keyEntitySets:["A_Project","A_ProjectElement"]}],queryTemplates:[{name:"active-projects",description:"List active projects",entitySet:"A_Project",filters:[],select:["Project","ProjectDescription","ResponsiblePerson","PlannedStartDate"],orderby:"PlannedStartDate desc",top:50},{name:"project-elements-by-project",description:"WBS elements for a specific project",entitySet:"A_ProjectElement",filters:[{field:"Project",operator:"eq",value:""}],select:["ProjectElement","ProjectElementDescription","PlannedStartDate","PlannedEndDate"],top:100}],suggestedTool:"sap_nl_query",commonQueries:["Active projects","WBS elements for a project"]},warehouse:{domain:"warehouse",description:"Warehouse resources, tasks, and storage management",services:[{name:"API_WAREHOUSE_RESOURCE",path:"/sap/opu/odata/sap/API_WAREHOUSE_RESOURCE",version:"v2",keyEntitySets:["WarehouseResource","WarehouseTask"]}],queryTemplates:[{name:"open-warehouse-tasks",description:"Warehouse tasks not yet completed",entitySet:"WarehouseTask",filters:[],select:["WarehouseTask","WarehouseTaskType","Warehouse","WarehouseStorageBin"],top:50},{name:"warehouse-resources-by-plant",description:"Warehouse resources for a specific plant",entitySet:"WarehouseResource",filters:[{field:"Plant",operator:"eq",value:""}],select:["WarehouseResource","Warehouse","Plant"],top:50}],suggestedTool:"sap_nl_query",commonQueries:["Open warehouse tasks","Warehouse resources by plant"]},analytics:{domain:"analytics",description:"Financial analytics, expense tracking, and revenue analysis",services:[{name:"API_OPLACCTG_SRV",path:"/sap/opu/odata/sap/API_OPLACCTG_SRV",version:"v2",keyEntitySets:["A_OperationalAcctgDocItem"]}],queryTemplates:[{name:"expenses-this-year",description:"Expense postings since start of current year",entitySet:"A_OperationalAcctgDocItem",filters:[{field:"PostingDate",operator:"ge",value:"$YEAR_START"}],select:["AccountingDocument","CompanyCode","GLAccount","AmountInCompanyCodeCurrency","PostingDate"],orderby:"PostingDate desc",top:100},{name:"monthly-revenue",description:"Revenue postings this month",entitySet:"A_OperationalAcctgDocItem",filters:[{field:"PostingDate",operator:"ge",value:"$MONTH_START"}],select:["AccountingDocument","CompanyCode","GLAccount","AmountInCompanyCodeCurrency","PostingDate"],orderby:"PostingDate desc",top:100}],suggestedTool:"sap_nl_query",commonQueries:["Expenses this year","Monthly revenue","Postings by G/L account"]},"master-data":{domain:"master-data",description:"Cross-domain master data: business partners, materials, cost centers",services:[{name:"API_BUSINESS_PARTNER",path:"/sap/opu/odata/sap/API_BUSINESS_PARTNER",version:"v2",keyEntitySets:["A_BusinessPartner"]},{name:"API_PRODUCT_SRV",path:"/sap/opu/odata/sap/API_PRODUCT_SRV",version:"v2",keyEntitySets:["A_Product"]},{name:"API_COSTCENTER_SRV",path:"/sap/opu/odata/sap/API_COSTCENTER_SRV",version:"v2",keyEntitySets:["A_CostCenter"]}],queryTemplates:[{name:"search-master-data-by-name",description:"Search business partners, materials, or cost centers by name",entitySet:"A_BusinessPartner",filters:[{field:"BusinessPartnerFullName",operator:"contains",value:""}],select:["BusinessPartner","BusinessPartnerFullName","BusinessPartnerCategory"],top:20},{name:"cost-centers-by-company",description:"List cost centers for a company code",entitySet:"A_CostCenter",filters:[{field:"CompanyCode",operator:"eq",value:""}],select:["CostCenter","CostCenterDescription","CompanyCode","ValidityStartDate"],top:100}],suggestedTool:"sap_nl_query",commonQueries:["Search master data by name","Cost centers by company code","Materials by type"]},"cross-application":{domain:"cross-application",description:"Attachments, change documents, and cross-module services",services:[{name:"API_ATTACHMENT_SRV",path:"/sap/opu/odata/sap/API_ATTACHMENT_SRV",version:"v2",keyEntitySets:["A_DocumentInfoRecord"]},{name:"API_BUSINESS_OBJECT_PAGE",path:"/sap/opu/odata/sap/API_BUSINESS_OBJECT_PAGE",version:"v2",keyEntitySets:["A_BusinessObjectPage"]}],queryTemplates:[{name:"attachments-for-object",description:"Find attachments linked to a business object",entitySet:"A_DocumentInfoRecord",filters:[{field:"LinkedSAPObjectKey",operator:"eq",value:""}],select:["DocumentInfoRecordDocNumber","DocumentInfoRecordDocType","LinkedSAPObjectKey"],top:50},{name:"recent-change-documents",description:"Change documents from the last month",entitySet:"A_BusinessObjectPage",filters:[{field:"LastChangeDate",operator:"ge",value:"$MONTH_AGO"}],select:["BusinessObjectType","LastChangeDate","LastChangedByUser"],orderby:"LastChangeDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Attachments for a business object","Recent change documents"]},unknown:{domain:"unknown",description:"Unknown or unclassified domain. Use service discovery to find available services.",services:[],queryTemplates:[{name:"discover-services",description:"No specific domain matched. Use sap_discover_services to explore available SAP services on this system.",entitySet:"",filters:[]},{name:"browse-domains",description:"Browse available routing domains with sap://routing/{domain} to find the right service for your query.",entitySet:"",filters:[]}],suggestedTool:"sap_discover_services",commonQueries:["Use sap_discover_services to find available services","Check sap://routing/{domain} with a specific domain"]}};var Ra={$TODAY:()=>pn(new Date),$YESTERDAY:()=>{let t=new Date;return t.setDate(t.getDate()-1),pn(t)},$MONTH_START:()=>{let t=new Date;return `${t.getFullYear()}-${mn(t.getMonth()+1)}-01`},$YEAR_START:()=>`${new Date().getFullYear()}-01-01`,$MONTH_AGO:()=>{let t=new Date;return t.setMonth(t.getMonth()-1),pn(t)}};function pn(t){return `${t.getFullYear()}-${mn(t.getMonth()+1)}-${mn(t.getDate())}`}function mn(t){return t.toString().padStart(2,"0")}function _a(t){if(typeof t!="string")return t;let e=Ra[t];return e?e():t}function Hr(t){return {...t,queryTemplates:t.queryTemplates.map(e=>({...e,filters:e.filters.map(n=>({...n,value:_a(n.value)}))}))}}function jr(t){let e=new ResourceTemplate("sap://routing/{domain}",{list:void 0});t.registerResource("sap-routing",e,{title:"SAP Domain Routing",description:"Service mappings and query templates for a business domain. Returns the relevant SAP services, entity sets, and pre-built query examples for a domain like business-partner, sales, or finance. Use this before building OData queries to know which service and entity set to target.",mimeType:"application/json"},async(n,r)=>{let s=r.domain,i=un[s];if(!i)return {contents:[{uri:n.toString(),mimeType:"application/json",text:JSON.stringify({error:`Unknown domain '${s}'`,availableDomains:Object.keys(un)},null,2)}]};let o=Hr(i);return {contents:[{uri:n.toString(),mimeType:"application/json",text:JSON.stringify({...o,note:"Service paths are typical defaults. Use sap_discover_services to verify actual paths on your system."},null,2)}]}});}var fn={core:["test-connection","sap_list_services","sap_get_metadata","sap_discover_services","sap_nl_query"],odata:["sap_read","sap_query","sap_create","sap_update","sap_delete","sap_function","sap_batch"],idoc:["sap_idoc_send","sap_idoc_status","sap_idoc_discover","sap_idoc_list_received"],rfc:["sap_rfc_call","sap_rfc_metadata","sap_bapi_call","sap_bapi_commit","sap_rfc_search_functions"]},Vr=new Set(["sap_create","sap_update","sap_delete","sap_function","sap_batch","sap_idoc_send","sap_rfc_call","sap_bapi_call","sap_bapi_commit"]),Wr=["core","odata"],qr=new Map;for(let[t,e]of Object.entries(fn))for(let n of e)qr.set(n,t);var vt=class{handles=new Map;activeTiers;options;constructor(e){this.options=e,this.activeTiers=new Set(e.activeTiers);}registerHandle(e,n){this.handles.set(e,n);}applyInitialVisibility(){for(let[e,n]of this.handles)this.shouldToolBeVisible(e)||n.disable();}enableTier(e){if(this.options.allowedTiers&&!this.options.allowedTiers.includes(e))return {error:`Cannot enable ${e} tier: not permitted by the token policy of this connection (allowed: ${this.options.allowedTiers.join(", ")})`};if(e==="idoc"&&!this.options.hasIdocConfig)return {error:"Cannot enable IDoc tier: no IDoc partner configuration provided (--sndprn, --rcvprn required)"};if(e==="rfc"&&!this.options.hasRfcConfig)return {error:"Cannot enable RFC tier: no RFC connection configuration provided (SAP_RFC_ASHOST + SAP_RFC_SYSNR, or SAP_RFC_MSHOST + SAP_RFC_GROUP required)"};this.activeTiers.add(e);let n=fn[e],r=[];for(let s of n){if(this.options.readOnly&&Vr.has(s))continue;let i=this.handles.get(s);i&&(i.enable(),r.push(s));}return {enabled:r,activeTiers:this.getActiveTiers()}}getActiveTiers(){return Array.from(this.activeTiers)}getAllowedTiers(){return this.options.allowedTiers?[...this.options.allowedTiers]:void 0}getToolsByTier(e){return fn[e]}isReadOnlyMode(){return this.options.readOnly}shouldToolBeVisible(e){let n=qr.get(e);return n?!(!this.activeTiers.has(n)||this.options.allowedTiers&&!this.options.allowedTiers.includes(n)||n==="idoc"&&!this.options.hasIdocConfig||n==="rfc"&&!this.options.hasRfcConfig||this.options.readOnly&&Vr.has(e)):true}};var Kr="/sap/opu/odata/sap",Oa=/^[A-Z0-9_/]+$/i,oe=class extends Error{guidance;constructor(e){super(`serviceUrl ist weder ein Pfad noch ein technischer Servicename: "${e}"`),this.name="InvalidServiceUrlError",this.guidance=`Erwartet wird ein Pfad wie ${Kr}/API_BUSINESS_PARTNER oder der technische Name (API_BUSINESS_PARTNER), den sap_discover_services als technicalName liefert.`;}};function H(t){let e=t.trim();if(e.length===0)throw new oe(t);if(e.startsWith("/"))return e.replace(/\/+$/,"")||"/";if(e.startsWith("http://")||e.startsWith("https://"))try{return new URL(e).pathname.replace(/\/+$/,"")}catch{throw new oe(t)}if(Oa.test(e))return `${Kr}/${e.replace(/^\/+|\/+$/g,"")}`;throw new oe(t)}var Fe=class extends Error{guidance;extra;constructor(e,n,r={}){super(e),this.name="ToolInputError",this.guidance=n,this.extra=r;}};function N(t,e){if(t instanceof oe)return gn({code:"INVALID_INPUT",error:t.message,guidance:t.guidance,...e?.hint?{hint:e.hint}:{}});if(t instanceof Fe)return gn({code:"INVALID_INPUT",error:t.message,...t.guidance?{guidance:t.guidance}:{},...t.extra,...e?.hint?{hint:e.hint}:{}});let n=pe(t,{serviceUrl:e?.serviceUrl});return gn({code:n.code,error:n.message,...n.httpStatus!==void 0?{httpStatus:n.httpStatus}:{},...n.sapCode?{sapCode:n.sapCode}:{},...n.guidance?{guidance:n.guidance}:{},...e?.hint?{hint:e.hint}:{}})}function gn(t){let e=le()?.correlationId;return {content:[{type:"text",text:JSON.stringify({...t,...e?{correlationId:e}:{}},null,2)}],isError:true}}function Gr(t,e,n){let r=n??new k,s=new Map;function i(a){let c=H(a),l=s.get(c);return l||(l=new M(e,c,r),s.set(c,l)),l}function o(a,c){return N(a,{serviceUrl:c})}t.registerTool("sap_batch",{title:"Execute SAP Batch Request",description:"Execute multiple OData operations in a single batch request. Supports V2 (multipart/mixed) and V4 (JSON) batch formats automatically. Operations can be grouped into changesets for transactional behavior (all succeed or all fail). Returns per-operation status -- individual failures are always reported, never hidden behind the batch HTTP 200.",inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),operations:z$2.array(z$2.object({method:z$2.enum(["get","post","patch","delete"]).describe("HTTP method"),entitySet:z$2.string().describe("Entity set name"),key:z$2.record(z$2.string(),z$2.unknown()).optional().describe("Entity key for read/update/delete"),data:z$2.record(z$2.string(),z$2.unknown()).optional().describe("Request body for create/update"),changesetId:z$2.string().optional().describe("Changeset group ID for transactional grouping")})).describe("Array of operations to execute in batch")}),annotations:{readOnlyHint:false,destructiveHint:true,idempotentHint:false,openWorldHint:true}},async({serviceUrl:a,operations:c})=>{try{let l=i(a),d=c,u=await l.batch(d);return {content:[{type:"text",text:JSON.stringify(u,null,2)}]}}catch(l){return o(l)}});}async function zr(t,e,n=5){if(t.length===0)return [];let r=Math.max(1,Math.min(Math.floor(n)||1,t.length)),s=new Array(t.length),i=0;async function o(){for(;;){let a=i++;if(a>=t.length)return;s[a]=await e(t[a],a);}}return await Promise.all(Array.from({length:r},()=>o())),s}var Ia=[".integrationsuite.","integrationsuite-trial.",".it-cpi.",".hci.",".prod.apimanagement.","-api.s4hana.ondemand.com"];function Yr(t){let e=t.toLowerCase();return Ia.some(n=>e.includes(n))}var V=class extends Error{constructor(n,r,s,i){super(n);this.httpStatus=r;this.sapGuidance=s;this.isBtpDetected=i;this.name="CatalogError";}},Da=new Set(["ECONNREFUSED","ETIMEDOUT","ENOTFOUND"]);function Na(t,e=false){switch(t){case 403:return "Catalog access denied. Check transaction PFCG for role assignment and SU53 for authorization trace. Required authorization object: S_SERVICE with SRV_NAME = IWFND_CATALOGSERVICE.";case 404:return e?"V4 catalog not available. Publish service group via /IWFND/V4_ADMIN.":"Catalog service not found. Activate it via transaction /IWFND/MAINT_SERVICE.";case 500:return "SAP system error. Check transaction ST22 for ABAP dumps and SM21 for system log.";default:return "Could not connect to SAP system. Verify the hostname, port, and network connectivity."}}function xa(t){switch(t){case "ECONNREFUSED":return "Connection refused. Verify that the SAP system is running and the port is correct.";case "ETIMEDOUT":return "Connection timeout. Check network connectivity and firewall rules.";case "ENOTFOUND":return "Hostname not found. Verify the SAP system hostname in your configuration.";default:return "Could not connect to SAP system. Verify the hostname, port, and network connectivity."}}function Xr(t){return typeof t=="object"&&t!==null&&"response"in t&&typeof t.response=="object"&&t.response!==null&&typeof t.response.status=="number"}function Ma(t){return typeof t=="object"&&t!==null&&"code"in t&&typeof t.code=="string"&&Da.has(t.code)}function He(t,e,n=false){let r=Yr(e);if(Xr(t)){let i=t.response.status,o=Na(i,n),a="message"in t&&typeof t.message=="string"?t.message:`HTTP ${i}`;return new V(a,i,o,r)}if(Ma(t)){let i=xa(t.code),o="message"in t&&typeof t.message=="string"?t.message:t.code;return new V(o,void 0,i,r)}let s=t instanceof Error?t.message:String(t);return new V(s,void 0,void 0,r)}function Jr(t){return Xr(t)&&t.response.status===404?"V4 catalog not available. Publish service group via /IWFND/V4_ADMIN.":t instanceof Error?t.message:String(t)}var Ua=["/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/iwfnd/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/IWFND/CATALOGSERVICE/ServiceCollection"];function $a(t){if(typeof t=="object"&&t!==null&&"response"in t)return t.response?.status}async function Qr(t,e,n){let r;for(let s of Ua)try{return await t.request({method:"get",url:s,params:{$format:"json",$top:"1"},expectedStatuses:[404]}),n?.onMatch?.(s),s}catch(i){if(i instanceof $)throw i;if($a(i)===404){r=i;continue}throw He(i,e,false)}throw He(r??new Error("All V2 catalog paths returned 404"),e,false)}function Zr(t){try{return new URL(t).pathname}catch{return t}}function Ba(t){let e=t.replace(/'/g,"''");return `substringof('${e}',TechnicalServiceName) or substringof('${e}',Title)`}var Et=class t{constructor(e,n,r={}){this.httpClient=e;this.cache=n;this.options=r;}static DEFAULT_V4_URL="/sap/opu/odata4/iwfnd/config/default/iwfnd/catalog/0002/ServiceGroups";resolvedV2Path;async discoverServices(e){let n=this.cache.get();if(n)return {services:n,warnings:[]};if(e){let l=await this.tryFilteredFetch(e);if(l)return l}let r=[],[s,i]=await zr(["v4","v2"],async l=>l==="v4"?await this.fetchV4Catalog().then(d=>({ok:true,services:d}),d=>({ok:false,error:d})):await this.fetchV2Catalog().then(d=>({ok:true,services:d}),d=>({ok:false,error:d})),this.options.concurrency??5),o=[];if(s?.ok)o=s.services;else if(s){if(s.error instanceof $)throw s.error;r.push(`V4 catalog fetch failed: ${Jr(s.error)}`);}if(!i?.ok){let l=i?.error;throw l instanceof V?l:He(l,this.options.baseUrl??"",false)}let a=i.services,c=this.mergeAndDeduplicate(o,a);return this.cache.set(c),{services:c,warnings:r}}async tryFilteredFetch(e){try{return {services:await this.fetchV2Catalog(Ba(e)),warnings:[],filtered:!0}}catch(n){if(n instanceof $)throw n;n?.response?.status;return void 0}}async fetchV2Catalog(e){let n=this.options.catalogV2Url??this.resolvedV2Path;return n||(n=await Qr(this.httpClient,this.options.baseUrl??"",{onMatch:this.options.onDiscoveryMatch}),this.resolvedV2Path=n),((await this.httpClient.request({method:"get",url:n,params:{$format:"json",...e?{$filter:e}:{}},...e?{expectedStatuses:[400,501]}:{}})).data?.d?.results??[]).map(o=>({technicalName:o.TechnicalServiceName,title:o.Title,serviceUrl:Zr(o.ServiceUrl),description:o.Description,version:"v2",serviceVersion:o.TechnicalServiceVersion}))}async fetchV4Catalog(){let e=this.options.catalogV4Url??t.DEFAULT_V4_URL,s=(await this.httpClient.request({method:"get",url:e,params:{$expand:"DefaultSystem($expand=Services)"},headers:{Accept:"application/json"},expectedStatuses:[404]})).data?.value??[],i=[];for(let o of s){let a=o.DefaultSystem?.Services??[];for(let c of a)i.push({technicalName:c.ServiceId??c.ServiceName??"",title:c.ServiceTitle??c.Title??"",serviceUrl:Zr(c.ServiceUrl),version:"v4",serviceVersion:c.ServiceVersion});}return i}mergeAndDeduplicate(e,n){let r=new Map;for(let s of e)r.set(s.technicalName,s);for(let s of n)r.has(s.technicalName)||r.set(s.technicalName,s);return [...r.values()]}};var Fa=[[/BUSINESS_?PARTNER|BUPA/i,"business-partner"],[/SALES_ORDER|BILLING|CREDIT_MEMO|DEBIT_MEMO|SALES_CONTRACT|SALES_QUOT/i,"sales"],[/PURCHASEORDER|PURCHASE_ORDER|PURCHASE_REQ|SUPPLIER|PURG/i,"procurement"],[/JOURNALENTRY|JOURNAL_ENTRY|OPLACCTG|COST_CENTER|PROFIT_CENTER|GL_ACCOUNT|FINANCIAL/i,"finance"],[/MATERIAL_DOC|MATERIAL_STOCK|MATERIAL_SRV|PRODUCT\b/i,"material"],[/OUTBOUND_DELIV|INBOUND_DELIV|SHIPPING|TRANSPORTATION/i,"logistics"],[/PRODUCTION_ORDER|PLANNED_ORDER|ROUTING|BOM\b/i,"production"],[/EMPLOYEE|WORKFORCE|PERSONNEL|PAYROLL/i,"hr"],[/INSPECTION|QUALITY/i,"quality"],[/MAINTENANCE|EQUIPMENT|FUNCTIONAL_LOC/i,"plant-maintenance"],[/PROJECT|WBS\b/i,"project"],[/WAREHOUSE|STORAGE_BIN/i,"warehouse"],[/ANALYTIC|CUBE/i,"analytics"]],Ha=[[/^[ZY]?MM[_]/i,"procurement"],[/^[ZY]?SD[_]/i,"sales"],[/^[ZY]?FI[_]|^[ZY]?CO[_]/i,"finance"],[/^[ZY]?PP[_]/i,"production"],[/^[ZY]?HR[_]|^[ZY]?PA[_]|^[ZY]?PT[_]/i,"hr"],[/^[ZY]?WM[_]/i,"warehouse"],[/^[ZY]?PM[_]/i,"plant-maintenance"],[/^[ZY]?LE[_]/i,"logistics"]];function ja(t){for(let[e,n]of Fa)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};for(let[e,n]of Ha)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};return {...t,category:"unknown",categoryConfidence:"low"}}function es(t){return t.map(ja)}function Ee(t,e){let n=Number.isFinite(t)&&t>0?Math.floor(t):50,r=Number.isFinite(e)&&e>0?Math.floor(e):0;return {top:Math.min(n,1e3),skip:r}}function Ce(t){let{returned:e,totalCount:n,paging:r,hasMoreHint:s}=t,i=s??(n!==void 0?r.skip+e<n:e>=r.top),o={returned:e,hasMore:i,top:r.top,skip:r.skip};return n!==void 0&&(o.totalCount=n),i&&(o.nextSkip=r.skip+e),o}function ts(t,e,n,r){let s=r??new G,i=new Et(e,s,n);t.registerTool("sap_discover_services",{title:"Discover SAP OData Services",description:`Discover all available OData services on the connected SAP system. Returns categorized services with search, category filtering, and pagination. Use this as the first step to explore what APIs are available before using sap_list_services or sap_get_metadata.
|
|
211
|
+
`),o=`multipart/mixed; boundary=${r}`;return {body:i,contentType:o}}var ga=new Set(["EntityType","Property","NavigationProperty","Association","EntitySet","AssociationSet","PropertyRef","End","FunctionImport","Parameter"]),ha=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",removeNSPrefix:true,processEntities:false,htmlEntities:false,isArray:t=>ga.has(t)});function an(t){return t===void 0?true:String(t).toLowerCase()!=="false"}function At(t,e){let r=ha.parse(t).Edmx.DataServices.Schema,s=Array.isArray(r)?r[0]:r,i=s.EntityType??[],o=s.Association??[],a=s.EntityContainer,c=a?.EntitySet??[],l=new Map;for(let g of o)l.set(g["@_Name"],g);let d=i.map(g=>{let h=ya(g.Property??[]),S=(g.Key?.PropertyRef??[]).map(b=>b["@_Name"]),T=h.filter(b=>S.includes(b.name)),y=Sa(g.NavigationProperty??[],l);return {name:g["@_Name"],keys:T,properties:h,navigationProperties:y}}),u=c.map(g=>({name:g["@_Name"],entityTypeName:cn(g["@_EntityType"]),creatable:an(g["@_creatable"]),updatable:an(g["@_updatable"]),deletable:an(g["@_deletable"])})),m=a?.FunctionImport??[],p=Ta(Array.isArray(m)?m:[m]),f={version:"v2",serviceUrl:e,entityTypes:d,entitySets:u};return p.length>0&&(f.functionImports=p),f}function ya(t){return t.map(e=>{let n={name:e["@_Name"],type:e["@_Type"],nullable:e["@_Nullable"]!=="false"};return e["@_MaxLength"]!==void 0&&(n.maxLength=Number.parseInt(e["@_MaxLength"],10)),e["@_Precision"]!==void 0&&(n.precision=Number.parseInt(e["@_Precision"],10)),e["@_Scale"]!==void 0&&(n.scale=Number.parseInt(e["@_Scale"],10)),n})}function Sa(t,e){return t.map(n=>{let r=cn(n["@_Relationship"]),s=e.get(r),i="Unknown",o=false;if(s){let a=s.End.find(c=>c["@_Role"]===n["@_ToRole"]);a&&(i=cn(a["@_Type"]),o=a["@_Multiplicity"]==="*");}return {name:n["@_Name"],targetEntityType:i,isCollection:o}})}function Ta(t){return t.filter(e=>e["@_Name"]).map(e=>{let n=(e.Parameter??[]).map(s=>({name:s["@_Name"],type:s["@_Type"],...s["@_Nullable"]==="true"?{nullable:true}:{}})),r={name:e["@_Name"],httpMethod:e["@_HttpMethod"]??"POST",parameters:n};return e["@_ReturnType"]&&(r.returnType=e["@_ReturnType"]),r})}function cn(t){let e=t.lastIndexOf(".");return e>=0?t.substring(e+1):t}var Aa=/^\/Date\((-?\d+)([+-]\d{4})?\)\/$/;function Le(t){if(!t||typeof t!="object")return {results:[]};let e=t;if("d"in e&&e.d&&typeof e.d=="object"){let n=e.d;if("results"in n&&Array.isArray(n.results)){let s={results:n.results};return n.__count!==void 0&&(s.count=Number(n.__count)),typeof n.__next=="string"&&(s.nextLink=n.__next),s}return {results:[{...n}]}}return "results"in e&&Array.isArray(e.results)?{results:e.results}:{results:[e]}}function Ir(t){let e=Aa.exec(t);if(!e)return t;let n=Number(e[1]);return new Date(n).toISOString()}function ie(t){if(t==null)return t;if(Array.isArray(t))return t.map(e=>ie(e));if(typeof t=="object"){let e=t,n={};for(let[r,s]of Object.entries(e))r!=="__metadata"&&(s&&typeof s=="object"&&!Array.isArray(s)&&"__deferred"in s||(typeof s=="string"?n[r]=Ir(s):n[r]=ie(s)));return n}return typeof t=="string"?Ir(t):t}var Dr=10,Be=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;}async read(e,n,r){let s=on(r??{});if(n){let i=`${this.serviceUrl}/${we(e,n)}`,o=await this.httpClient.request({method:"get",url:i,headers:{Accept:"application/json"},params:s});o.headers.etag&&this.etagCache.set(e,n,o.headers.etag);let a=Le(o.data);return {...a,results:a.results.map(c=>ie(c))}}return this.fetchWithPagination(`${this.serviceUrl}/${e}`,s,r?.maxPages??Dr)}async query(e,n){let r=on(n,true),s=n.maxPages??Dr;return this.fetchWithPagination(`${this.serviceUrl}/${e}`,r,s)}async create(e,n){let r=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/${e}`,data:n,headers:{Accept:"application/json","Content-Type":"application/json"}}),s=Le(r.data);return s.results.length>0?ie(s.results[0]):{}}async update(e,n,r){let s=`${this.serviceUrl}/${we(e,n)}`,i=this.etagCache.get(e,n)??"*",o=await this.httpClient.request({method:"post",url:s,data:r,headers:{Accept:"application/json","Content-Type":"application/json","X-HTTP-Method":"MERGE","If-Match":i}});o.headers.etag&&this.etagCache.set(e,n,o.headers.etag);}async delete(e,n){let r=`${this.serviceUrl}/${we(e,n)}`,s=this.etagCache.get(e,n)??"*";await this.httpClient.request({method:"delete",url:r,headers:{Accept:"application/json","If-Match":s}}),this.etagCache.update(e,n,void 0);}async functionImport(e,n,r){let s=(r??"POST").toLowerCase(),i={$format:"json"};if(n)for(let[l,d]of Object.entries(n))typeof d=="string"?i[l]=`'${d}'`:i[l]=String(d);let o=await this.httpClient.request({method:s,url:`${this.serviceUrl}/${e}`,headers:{Accept:"application/json"},params:i}),a=Le(o.data);if(a.results.length>0)return ie(a.results[0]);let c=o.data;return c?.d&&typeof c.d=="object"?c.d:{}}async batch(e,n){let{body:r,contentType:s}=Or(this.serviceUrl,e,n),i=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:r,headers:{"Content-Type":s,Accept:"multipart/mixed"}}),o=i.headers["content-type"]??s,a=Pr(i.data,o),c=[...e,...(n??[]).flatMap(l=>l.operations)];for(let l=0;l<a.length&&l<c.length;l++)a[l].entitySet=c[l].entitySet;return {results:a}}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml"}});return At(e.data,this.serviceUrl)}async fetchWithPagination(e,n,r){let s=[],i,o=e,a=n,c=0,l=false;for(;o&&c<r;){if(c>0&&Je()){l=true;break}let m=(await this.httpClient.request({method:"get",url:o,headers:{Accept:"application/json"},params:a})).data,p=Le(m),f=p.results.map(h=>ie(h));s.push(...f),c===0&&p.count!==void 0&&(i=p.count),c++;let g=m?.d;g&&typeof g.__next=="string"?(o=g.__next,a=void 0):o=void 0;}let d={results:s};return i!==void 0&&(d.count=i),o&&(d.nextLink=o),l&&(d.truncated=true,d.truncationReason=`Nach ${c} Seite(n) abgebrochen: Zeitbudget des Tool-Aufrufs ersch\xF6pft oder Client hat die Verbindung verworfen. Mit skip=${s.length} weiterlesen.`),d}};function Nr(t){let n=(typeof t=="string"?JSON.parse(t):t).responses;return !n||!Array.isArray(n)?[]:n.map(r=>{let s=r.status,i=s>=200&&s<300,o={status:s,entitySet:""};return r.body!==void 0&&r.body!==null&&(i?o.result=r.body:o.error=JSON.stringify(r.body)),o})}function xr(t){return typeof t=="string"?`'${t}'`:String(t)}function z(t,e){let n=Object.entries(e);if(n.length===1){let[,s]=n[0];return `${t}(${xr(s)})`}let r=n.map(([s,i])=>`${s}=${xr(i)}`).join(",");return `${t}(${r})`}function bt(t,e){let n={};return t.filter&&(n.$filter=t.filter),t.select?.length&&(n.$select=t.select.join(",")),t.expand?.length&&(n.$expand=t.expand.join(",")),t.orderby&&(n.$orderby=t.orderby),t.top!==void 0&&(n.$top=String(t.top)),t.skip!==void 0&&(n.$skip=String(t.skip)),e&&(n.$count="true"),n}function Mr(t,e){let n=[];for(let s=0;s<e.length;s++){let i=e[s],o=i.key?z(i.entitySet,i.key):i.entitySet,a={id:String(s+1),method:i.method.toUpperCase(),url:o};i.changesetId&&(a.atomicityGroup=i.changesetId),i.data&&(i.method==="post"||i.method==="patch"||i.method==="delete")&&(a.headers={"Content-Type":"application/json"},a.body=i.data),n.push(a);}return {body:JSON.stringify({requests:n}),contentType:"application/json"}}function Ur(t){let e=t.split(".");return e[e.length-1]}function ba(t,e){return {name:t,type:e.$Type??"Edm.String",nullable:e.$Nullable??false,maxLength:e.$MaxLength,precision:e.$Precision,scale:e.$Scale}}function $r(t,e){let n=JSON.parse(t),r=[],s=[],i=[];for(let[a,c]of Object.entries(n)){if(a.startsWith("$"))continue;let l=c;for(let[d,u]of Object.entries(l)){let m=u;if(m.$Kind==="Action"||m.$Kind==="Function"){let p=m.$IsBound??false,f=m.$Parameter??[],g=p&&f.length>0?f[0].$Name:void 0,h=(p?f.slice(1):f).map(T=>({name:T.$Name,type:T.$Type??"Edm.String",...T.$Nullable===true?{nullable:true}:{}})),S=m.$ReturnType?m.$ReturnType.$Type:void 0;i.push({name:d,isBound:p,...g?{bindingParameter:g}:{},parameters:h,...S?{returnType:S}:{}});}else if(m.$Kind==="EntityType"){let p=[],f=[];for(let[S,T]of Object.entries(m)){if(S.startsWith("$"))continue;let y=T;y.$Kind==="NavigationProperty"?f.push({name:S,targetEntityType:Ur(y.$Type),isCollection:y.$Collection??false}):p.push(ba(S,y));}let h=(m.$Key??[]).map(S=>{let T=p.find(y=>y.name===S);return T||{name:S,type:"Edm.String",nullable:false}});r.push({name:d,keys:h,properties:p,navigationProperties:f});}else if(m.$Kind==="EntityContainer")for(let[p,f]of Object.entries(m)){if(p.startsWith("$"))continue;let g=f;g.$Collection&&g.$Type&&s.push({name:p,entityTypeName:Ur(g.$Type),creatable:dn(g,"InsertRestrictions","Insertable"),updatable:dn(g,"UpdateRestrictions","Updatable"),deletable:dn(g,"DeleteRestrictions","Deletable")});}}}let o={version:"v4",serviceUrl:e,entityTypes:r,entitySets:s};return i.length>0&&(o.actions=i),o}function dn(t,e,n){for(let[r,s]of Object.entries(t)){if(!r.startsWith("@")||!r.endsWith(e))continue;let i=s?.[n];if(typeof i=="boolean")return i}return true}var va=10,vt=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;}async read(e,n,r){if(n){let l=`${this.serviceUrl}/${z(e,n)}`,d=r?bt(r):void 0,u=await this.httpClient.request({method:"get",url:l,params:d}),m=u.data,p=u.headers.etag??u.headers.ETag;return p&&this.etagCache.set(e,n,p),{results:[m]}}let s=r?bt(r):void 0,o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=o.value??[],c=o["@odata.count"];return {results:a,count:c}}async query(e,n){let r=n.maxPages??va,s=bt(n,true),o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=[...o.value??[]],c=o["@odata.count"],l=o["@odata.nextLink"],d=1,u=false;for(;l&&d<r;){if(Je()){u=true;break}let p=this.resolveNextLink(l),g=(await this.httpClient.request({method:"get",url:p})).data,h=g.value??[];a.push(...h),l=g["@odata.nextLink"],d++;}let m={results:a,count:c};return l&&(m.nextLink=l),u&&(m.truncated=true,m.truncationReason=`Nach ${d} Seite(n) abgebrochen: Zeitbudget des Tool-Aufrufs ersch\xF6pft oder Client hat die Verbindung verworfen. Mit skip=${a.length} weiterlesen.`),m}async create(e,n){return (await this.httpClient.request({method:"post",url:`${this.serviceUrl}/${e}`,data:n,headers:{"Content-Type":"application/json"}})).data}async update(e,n,r){let s=`${this.serviceUrl}/${z(e,n)}`,i=this.etagCache.get(e,n)??"*",o=await this.httpClient.request({method:"patch",url:s,data:r,headers:{"Content-Type":"application/json","If-Match":i}}),a=o.headers.etag??o.headers.ETag;a&&this.etagCache.update(e,n,a);}async delete(e,n){let r=`${this.serviceUrl}/${z(e,n)}`,s=this.etagCache.get(e,n)??"*";await this.httpClient.request({method:"delete",url:r,headers:{"If-Match":s}});}async batch(e){let{body:n,contentType:r}=Mr(this.serviceUrl,e),s=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:n,headers:{"Content-Type":r,Accept:"application/json"}}),i=Nr(s.data);for(let o=0;o<i.length&&o<e.length;o++)i[o].entitySet=e[o].entitySet;return {results:i}}async callAction(e,n,r,s){let i;return r&&s?i=`${this.serviceUrl}/${z(r,s)}/${e}`:i=`${this.serviceUrl}/${e}`,(await this.httpClient.request({method:"post",url:i,data:n??{},headers:{"Content-Type":"application/json"}})).data??{}}async callFunction(e,n,r,s){let i=this.buildFunctionParams(n),o;return r&&s?o=`${this.serviceUrl}/${z(r,s)}/${e}(${i})`:o=`${this.serviceUrl}/${e}(${i})`,(await this.httpClient.request({method:"get",url:o})).data??{}}buildFunctionParams(e){return !e||Object.keys(e).length===0?"":Object.entries(e).map(([n,r])=>typeof r=="string"?`${n}='${r}'`:`${n}=${String(r)}`).join(",")}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/json"}}),n=typeof e.data=="string"?e.data:JSON.stringify(e.data);return $r(n,this.serviceUrl)}resolveNextLink(e){return e.startsWith("http://")||e.startsWith("https://")?e:`${this.serviceUrl}/${e}`}};function ln(t,e){let n=t.trim();if(n.startsWith("{"))try{if("$Version"in JSON.parse(n))return "v4"}catch{}if(n.startsWith("<?xml")||n.startsWith("<edmx:Edmx")||n.startsWith("<Edmx"))return "v2";if(e){if(e.includes("/odata4/"))return "v4";if(e.includes("/odata/"))return "v2"}return "v2"}var M=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.metadataCache=r??new k;}protocolClient=null;version=null;initPromise=null;etagCache=new Tt;metadataCache;async ensureInitialized(){if(!this.protocolClient)return this.initPromise?this.initPromise:(this.initPromise=this.initialize(),this.initPromise)}async initialize(){let e=this.metadataCache.get(this.serviceUrl);if(e){this.version=e.version,this.protocolClient=this.createProtocolClient(e.version);return}let n="",r=false;try{let s=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml, application/json"}});n=typeof s.data=="string"?s.data:JSON.stringify(s.data),this.version=ln(n,this.serviceUrl),r=!0;}catch{this.version=ln("",this.serviceUrl);}if(this.protocolClient=this.createProtocolClient(this.version),r&&this.version==="v2"&&n)try{let s=At(n,this.serviceUrl);this.metadataCache.set(this.serviceUrl,s);}catch{}}createProtocolClient(e){return e==="v4"?new vt(this.httpClient,this.serviceUrl,this.etagCache):new Be(this.httpClient,this.serviceUrl,this.etagCache)}async getClient(){if(await this.ensureInitialized(),!this.protocolClient)throw new Error("ODataClient initialization failed: protocol client not created");return this.protocolClient}async read(e,n,r){return (await this.getClient()).read(e,n,r)}async query(e,n){return (await this.getClient()).query(e,n)}async create(e,n){return (await this.getClient()).create(e,n)}async update(e,n,r){return (await this.getClient()).update(e,n,r)}async delete(e,n){return (await this.getClient()).delete(e,n)}async batch(e,n){let r=await this.getClient();return this.version==="v4"?r.batch(e):r.batch(e,n)}async functionImport(e,n,r){let s=await this.getClient();if(s instanceof Be)return s.functionImport(e,n,r?.httpMethod);let i=s;return r?.isFunction?i.callFunction(e,n,r?.entitySet,r?.key):i.callAction(e,n,r?.entitySet,r?.key)}async getMetadata(){let e=this.metadataCache.get(this.serviceUrl);if(e)return e;let n=await this.getClient();if(e=this.metadataCache.get(this.serviceUrl),e)return e;let r=await n.getMetadata();return this.metadataCache.set(this.serviceUrl,r),r}getVersion(){return this.version}};function Lr(t,e,n){let r=n??new k,s=new ResourceTemplate("sap://schema/{serviceUrl}/{entitySet}",{list:void 0});t.registerResource("sap-entity-schema",s,{title:"SAP Entity Schema for Query Building",description:"Property names, types, and keys for an entity set \u2014 optimized context for building OData queries. Includes a sample filter in the correct V2/V4 syntax.",mimeType:"application/json"},async(i,o)=>{let a=decodeURIComponent(o.serviceUrl),c=decodeURIComponent(o.entitySet),d=await new M(e,a,r).getMetadata(),u=d.entitySets.find(h=>h.name===c);if(!u)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity set '${c}' not found in service ${a}`,availableEntitySets:d.entitySets.map(h=>h.name)},null,2)}]};let m=d.entityTypes.find(h=>h.name===u.entityTypeName);if(!m)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity type '${u.entityTypeName}' not found in metadata`},null,2)}]};let p=new Set(m.keys.map(h=>h.name)),f=m.properties.map(h=>({name:h.name,type:h.type,isKey:p.has(h.name)})),g=Ea(m.properties,d.version);return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({entitySet:c,version:d.version,properties:f,sampleFilter:g},null,2)}]}});}function Ea(t,e){let n=t.find(r=>r.type==="Edm.String");return n?e==="v2"?`${n.name} eq 'example' or substringof('example',${n.name})`:`${n.name} eq 'example' or contains(${n.name},'example')`:"PropertyName eq 'value'"}function Br(t,e){let n=new ResourceTemplate("idoc://received/{docnum}",{list:void 0});t.registerResource("idoc-received",n,{title:"Received IDoc",description:"Received IDoc data from webhook",mimeType:"application/json"},async(r,s)=>{let i=s.docnum,o=e.get(i);return o?{contents:[{uri:r.toString(),mimeType:"application/json",text:JSON.stringify({docnum:o.docnum,idocType:o.idocType,mesType:o.mesType,receivedAt:o.receivedAt.toISOString(),parsed:o.parsed},null,2)}]}:{contents:[{uri:r.toString(),mimeType:"application/json",text:JSON.stringify({error:`IDoc with docnum '${i}' not found`},null,2)}]}});}function Fr(t,e,n){let r=n??new k,s=new ResourceTemplate("sap://metadata/{serviceUrl}",{list:void 0});t.registerResource("sap-metadata",s,{title:"SAP Service Metadata",description:"Entity types, properties, keys, and navigation properties for an SAP OData service",mimeType:"application/json"},async(i,o)=>{let a=decodeURIComponent(o.serviceUrl),l=await new M(e,a,r).getMetadata();return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({version:l.version,serviceUrl:l.serviceUrl,entityTypes:l.entityTypes,entitySets:l.entitySets},null,2)}]}});}var un={"business-partner":{domain:"business-partner",description:"Customer, supplier, and business partner master data",services:[{name:"API_BUSINESS_PARTNER",path:"/sap/opu/odata/sap/API_BUSINESS_PARTNER",version:"v2",keyEntitySets:["A_BusinessPartner","A_BusinessPartnerAddress","A_Customer","A_Supplier"]}],queryTemplates:[{name:"find-customer-by-name",description:"Find customers by name (substring search)",entitySet:"A_Customer",filters:[{field:"CustomerName",operator:"contains",value:""}],select:["Customer","CustomerName","CustomerFullName","Country"],top:20},{name:"find-supplier-by-country",description:"Find suppliers in a specific country",entitySet:"A_Supplier",filters:[{field:"Country",operator:"eq",value:""}],select:["Supplier","SupplierName","Country","Region"],top:50}],suggestedTool:"sap_nl_query",commonQueries:["Find customers by name","List suppliers in a country","Get business partner addresses"]},sales:{domain:"sales",description:"Sales orders, billing documents, and revenue tracking",services:[{name:"API_SALES_ORDER_SRV",path:"/sap/opu/odata/sap/API_SALES_ORDER_SRV",version:"v2",keyEntitySets:["A_SalesOrder","A_SalesOrderItem"]},{name:"API_BILLING_DOCUMENT_SRV",path:"/sap/opu/odata/sap/API_BILLING_DOCUMENT_SRV",version:"v2",keyEntitySets:["A_BillingDocument"]}],queryTemplates:[{name:"recent-sales-orders",description:"Sales orders created this month",entitySet:"A_SalesOrder",filters:[{field:"CreationDate",operator:"ge",value:"$MONTH_START"}],select:["SalesOrder","SalesOrderType","SoldToParty","TotalNetAmount","CreationDate"],orderby:"CreationDate desc",top:50},{name:"overdue-invoices",description:"Billing documents past due date",entitySet:"A_BillingDocument",filters:[{field:"BillingDocumentDate",operator:"lt",value:"$TODAY"}],select:["BillingDocument","BillingDocumentType","SoldToParty","TotalNetAmount","BillingDocumentDate"],orderby:"BillingDocumentDate asc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Recent sales orders","Overdue invoices","Sales by customer"]},finance:{domain:"finance",description:"Journal entries, accounting documents, and financial postings",services:[{name:"API_JOURNALENTRY_SRV",path:"/sap/opu/odata/sap/API_JOURNALENTRY_SRV",version:"v2",keyEntitySets:["A_JournalEntry"]},{name:"API_OPLACCTG_SRV",path:"/sap/opu/odata/sap/API_OPLACCTG_SRV",version:"v2",keyEntitySets:["A_OperationalAcctgDocItem"]}],queryTemplates:[{name:"recent-journal-entries",description:"Journal entries posted this month",entitySet:"A_JournalEntry",filters:[{field:"DocumentDate",operator:"ge",value:"$MONTH_START"}],select:["AccountingDocument","CompanyCode","DocumentDate","PostingDate","DocumentType"],orderby:"DocumentDate desc",top:50},{name:"open-items-by-account",description:"Open accounting items for a G/L account",entitySet:"A_OperationalAcctgDocItem",filters:[{field:"GLAccount",operator:"eq",value:""}],select:["AccountingDocument","CompanyCode","GLAccount","AmountInCompanyCodeCurrency","PostingDate"],top:100}],suggestedTool:"sap_nl_query",commonQueries:["Recent journal entries","Open items by account","Accounting documents this month"]},procurement:{domain:"procurement",description:"Purchase orders, purchase requisitions, and supplier management",services:[{name:"API_PURCHASEORDER_PROCESS_SRV",path:"/sap/opu/odata/sap/API_PURCHASEORDER_PROCESS_SRV",version:"v2",keyEntitySets:["A_PurchaseOrder","A_PurchaseOrderItem"]},{name:"API_PURCHASEREQ_PROCESS_SRV",path:"/sap/opu/odata/sap/API_PURCHASEREQ_PROCESS_SRV",version:"v2",keyEntitySets:["A_PurchaseRequisition"]}],queryTemplates:[{name:"open-purchase-orders",description:"Purchase orders not yet fully delivered",entitySet:"A_PurchaseOrder",filters:[],select:["PurchaseOrder","PurchaseOrderType","Supplier","PurchasingOrganization","CreationDate"],orderby:"CreationDate desc",top:50},{name:"recent-purchase-requisitions",description:"Purchase requisitions created this month",entitySet:"A_PurchaseRequisition",filters:[{field:"CreationDate",operator:"ge",value:"$MONTH_START"}],select:["PurchaseRequisition","PurchaseRequisitionType","CreationDate"],orderby:"CreationDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Open purchase orders","Recent purchase requisitions","Purchase orders by supplier"]},material:{domain:"material",description:"Material master data and material document movements",services:[{name:"API_PRODUCT_SRV",path:"/sap/opu/odata/sap/API_PRODUCT_SRV",version:"v2",keyEntitySets:["A_Product"]},{name:"API_MATERIAL_DOCUMENT_SRV",path:"/sap/opu/odata/sap/API_MATERIAL_DOCUMENT_SRV",version:"v2",keyEntitySets:["A_MaterialDocumentHeader"]}],queryTemplates:[{name:"find-material-by-description",description:"Search materials by description text",entitySet:"A_Product",filters:[{field:"ProductDescription",operator:"contains",value:""}],select:["Product","ProductType","ProductDescription","BaseUnit"],top:20},{name:"recent-material-movements",description:"Material documents created this month",entitySet:"A_MaterialDocumentHeader",filters:[{field:"DocumentDate",operator:"ge",value:"$MONTH_START"}],select:["MaterialDocument","MaterialDocumentYear","DocumentDate","PostingDate"],orderby:"DocumentDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Find material by description","Recent material movements","Material stock levels"]},logistics:{domain:"logistics",description:"Inbound and outbound deliveries, shipping, and transportation",services:[{name:"API_INBOUND_DELIVERY_SRV",path:"/sap/opu/odata/sap/API_INBOUND_DELIVERY_SRV",version:"v2",keyEntitySets:["A_InbDeliveryHeader"]},{name:"API_OUTBOUND_DELIVERY_SRV",path:"/sap/opu/odata/sap/API_OUTBOUND_DELIVERY_SRV",version:"v2",keyEntitySets:["A_OutbDeliveryHeader"]}],queryTemplates:[{name:"pending-inbound-deliveries",description:"Inbound deliveries not yet received",entitySet:"A_InbDeliveryHeader",filters:[],select:["DeliveryDocument","Supplier","DeliveryDate","ReceivingPlant"],orderby:"DeliveryDate asc",top:50},{name:"recent-outbound-deliveries",description:"Outbound deliveries created this month",entitySet:"A_OutbDeliveryHeader",filters:[{field:"CreationDate",operator:"ge",value:"$MONTH_START"}],select:["DeliveryDocument","SoldToParty","CreationDate","DeliveryDate"],orderby:"CreationDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Pending inbound deliveries","Recent outbound deliveries","Deliveries by ship-to party"]},hr:{domain:"hr",description:"Employee master data and organizational assignments",services:[{name:"API_BUSINESS_PARTNER",path:"/sap/opu/odata/sap/API_BUSINESS_PARTNER",version:"v2",keyEntitySets:["A_BusinessPartner"]}],queryTemplates:[{name:"find-employee-by-name",description:"Search employees by name (BP category 2 = Person)",entitySet:"A_BusinessPartner",filters:[{field:"BusinessPartnerCategory",operator:"eq",value:"2"},{field:"BusinessPartnerFullName",operator:"contains",value:""}],select:["BusinessPartner","BusinessPartnerFullName","BusinessPartnerCategory","OrganizationBPName1"],top:20},{name:"employees-by-department",description:"List employees assigned to a department/organization",entitySet:"A_BusinessPartner",filters:[{field:"BusinessPartnerCategory",operator:"eq",value:"2"},{field:"OrganizationBPName1",operator:"contains",value:""}],select:["BusinessPartner","BusinessPartnerFullName","OrganizationBPName1"],top:50}],suggestedTool:"sap_nl_query",commonQueries:["Find employee by name","Employees by department"]},production:{domain:"production",description:"Production orders, planned orders, and manufacturing execution",services:[{name:"API_PLANNED_ORDER",path:"/sap/opu/odata/sap/API_PLANNED_ORDER",version:"v2",keyEntitySets:["PlannedOrder"]},{name:"API_PRODUCTION_ORDER",path:"/sap/opu/odata/sap/API_PRODUCTION_ORDER",version:"v2",keyEntitySets:["A_ProductionOrder"]}],queryTemplates:[{name:"open-production-orders",description:"Production orders not yet completed",entitySet:"A_ProductionOrder",filters:[],select:["ManufacturingOrder","ManufacturingOrderType","Material","ProductionPlant","MfgOrderPlannedStartDate"],orderby:"MfgOrderPlannedStartDate asc",top:50},{name:"planned-orders-this-month",description:"Planned orders created this month",entitySet:"PlannedOrder",filters:[{field:"PlannedOrderCreationDate",operator:"ge",value:"$MONTH_START"}],select:["PlannedOrder","Material","ProductionPlant","PlannedOrderCreationDate"],orderby:"PlannedOrderCreationDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Open production orders","Planned orders this month","Production orders by material"]},quality:{domain:"quality",description:"Quality inspections, inspection lots, and quality management",services:[{name:"API_QUALITYINSPECTION",path:"/sap/opu/odata/sap/API_QUALITYINSPECTION",version:"v2",keyEntitySets:["QualityInspection"]}],queryTemplates:[{name:"open-inspections",description:"Quality inspections not yet completed",entitySet:"QualityInspection",filters:[],select:["InspectionLot","Material","Plant","InspectionLotStartDate"],orderby:"InspectionLotStartDate asc",top:50},{name:"inspections-this-month",description:"Quality inspections started this month",entitySet:"QualityInspection",filters:[{field:"InspectionLotStartDate",operator:"ge",value:"$MONTH_START"}],select:["InspectionLot","Material","Plant","InspectionLotStartDate"],orderby:"InspectionLotStartDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Open quality inspections","Inspections this month"]},"plant-maintenance":{domain:"plant-maintenance",description:"Maintenance notifications, orders, and equipment management",services:[{name:"API_MAINTNOTIFICATION",path:"/sap/opu/odata/sap/API_MAINTNOTIFICATION",version:"v2",keyEntitySets:["MaintenanceNotification"]},{name:"API_MAINTENANCEORDER",path:"/sap/opu/odata/sap/API_MAINTENANCEORDER",version:"v2",keyEntitySets:["MaintenanceOrder"]}],queryTemplates:[{name:"open-maintenance-notifications",description:"Maintenance notifications not yet completed",entitySet:"MaintenanceNotification",filters:[],select:["MaintenanceNotification","NotificationType","FunctionalLocation","ReportedByUser","CreationDate"],orderby:"CreationDate desc",top:50},{name:"maintenance-orders-this-month",description:"Maintenance orders created this month",entitySet:"MaintenanceOrder",filters:[{field:"CreationDate",operator:"ge",value:"$MONTH_START"}],select:["MaintenanceOrder","MaintenanceOrderType","FunctionalLocation","CreationDate"],orderby:"CreationDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Open maintenance notifications","Maintenance orders this month","Notifications by functional location"]},project:{domain:"project",description:"Project systems, WBS elements, and project planning",services:[{name:"API_PROJECT",path:"/sap/opu/odata/sap/API_PROJECT",version:"v2",keyEntitySets:["A_Project","A_ProjectElement"]}],queryTemplates:[{name:"active-projects",description:"List active projects",entitySet:"A_Project",filters:[],select:["Project","ProjectDescription","ResponsiblePerson","PlannedStartDate"],orderby:"PlannedStartDate desc",top:50},{name:"project-elements-by-project",description:"WBS elements for a specific project",entitySet:"A_ProjectElement",filters:[{field:"Project",operator:"eq",value:""}],select:["ProjectElement","ProjectElementDescription","PlannedStartDate","PlannedEndDate"],top:100}],suggestedTool:"sap_nl_query",commonQueries:["Active projects","WBS elements for a project"]},warehouse:{domain:"warehouse",description:"Warehouse resources, tasks, and storage management",services:[{name:"API_WAREHOUSE_RESOURCE",path:"/sap/opu/odata/sap/API_WAREHOUSE_RESOURCE",version:"v2",keyEntitySets:["WarehouseResource","WarehouseTask"]}],queryTemplates:[{name:"open-warehouse-tasks",description:"Warehouse tasks not yet completed",entitySet:"WarehouseTask",filters:[],select:["WarehouseTask","WarehouseTaskType","Warehouse","WarehouseStorageBin"],top:50},{name:"warehouse-resources-by-plant",description:"Warehouse resources for a specific plant",entitySet:"WarehouseResource",filters:[{field:"Plant",operator:"eq",value:""}],select:["WarehouseResource","Warehouse","Plant"],top:50}],suggestedTool:"sap_nl_query",commonQueries:["Open warehouse tasks","Warehouse resources by plant"]},analytics:{domain:"analytics",description:"Financial analytics, expense tracking, and revenue analysis",services:[{name:"API_OPLACCTG_SRV",path:"/sap/opu/odata/sap/API_OPLACCTG_SRV",version:"v2",keyEntitySets:["A_OperationalAcctgDocItem"]}],queryTemplates:[{name:"expenses-this-year",description:"Expense postings since start of current year",entitySet:"A_OperationalAcctgDocItem",filters:[{field:"PostingDate",operator:"ge",value:"$YEAR_START"}],select:["AccountingDocument","CompanyCode","GLAccount","AmountInCompanyCodeCurrency","PostingDate"],orderby:"PostingDate desc",top:100},{name:"monthly-revenue",description:"Revenue postings this month",entitySet:"A_OperationalAcctgDocItem",filters:[{field:"PostingDate",operator:"ge",value:"$MONTH_START"}],select:["AccountingDocument","CompanyCode","GLAccount","AmountInCompanyCodeCurrency","PostingDate"],orderby:"PostingDate desc",top:100}],suggestedTool:"sap_nl_query",commonQueries:["Expenses this year","Monthly revenue","Postings by G/L account"]},"master-data":{domain:"master-data",description:"Cross-domain master data: business partners, materials, cost centers",services:[{name:"API_BUSINESS_PARTNER",path:"/sap/opu/odata/sap/API_BUSINESS_PARTNER",version:"v2",keyEntitySets:["A_BusinessPartner"]},{name:"API_PRODUCT_SRV",path:"/sap/opu/odata/sap/API_PRODUCT_SRV",version:"v2",keyEntitySets:["A_Product"]},{name:"API_COSTCENTER_SRV",path:"/sap/opu/odata/sap/API_COSTCENTER_SRV",version:"v2",keyEntitySets:["A_CostCenter"]}],queryTemplates:[{name:"search-master-data-by-name",description:"Search business partners, materials, or cost centers by name",entitySet:"A_BusinessPartner",filters:[{field:"BusinessPartnerFullName",operator:"contains",value:""}],select:["BusinessPartner","BusinessPartnerFullName","BusinessPartnerCategory"],top:20},{name:"cost-centers-by-company",description:"List cost centers for a company code",entitySet:"A_CostCenter",filters:[{field:"CompanyCode",operator:"eq",value:""}],select:["CostCenter","CostCenterDescription","CompanyCode","ValidityStartDate"],top:100}],suggestedTool:"sap_nl_query",commonQueries:["Search master data by name","Cost centers by company code","Materials by type"]},"cross-application":{domain:"cross-application",description:"Attachments, change documents, and cross-module services",services:[{name:"API_ATTACHMENT_SRV",path:"/sap/opu/odata/sap/API_ATTACHMENT_SRV",version:"v2",keyEntitySets:["A_DocumentInfoRecord"]},{name:"API_BUSINESS_OBJECT_PAGE",path:"/sap/opu/odata/sap/API_BUSINESS_OBJECT_PAGE",version:"v2",keyEntitySets:["A_BusinessObjectPage"]}],queryTemplates:[{name:"attachments-for-object",description:"Find attachments linked to a business object",entitySet:"A_DocumentInfoRecord",filters:[{field:"LinkedSAPObjectKey",operator:"eq",value:""}],select:["DocumentInfoRecordDocNumber","DocumentInfoRecordDocType","LinkedSAPObjectKey"],top:50},{name:"recent-change-documents",description:"Change documents from the last month",entitySet:"A_BusinessObjectPage",filters:[{field:"LastChangeDate",operator:"ge",value:"$MONTH_AGO"}],select:["BusinessObjectType","LastChangeDate","LastChangedByUser"],orderby:"LastChangeDate desc",top:50}],suggestedTool:"sap_nl_query",commonQueries:["Attachments for a business object","Recent change documents"]},unknown:{domain:"unknown",description:"Unknown or unclassified domain. Use service discovery to find available services.",services:[],queryTemplates:[{name:"discover-services",description:"No specific domain matched. Use sap_discover_services to explore available SAP services on this system.",entitySet:"",filters:[]},{name:"browse-domains",description:"Browse available routing domains with sap://routing/{domain} to find the right service for your query.",entitySet:"",filters:[]}],suggestedTool:"sap_discover_services",commonQueries:["Use sap_discover_services to find available services","Check sap://routing/{domain} with a specific domain"]}};var Ra={$TODAY:()=>pn(new Date),$YESTERDAY:()=>{let t=new Date;return t.setDate(t.getDate()-1),pn(t)},$MONTH_START:()=>{let t=new Date;return `${t.getFullYear()}-${mn(t.getMonth()+1)}-01`},$YEAR_START:()=>`${new Date().getFullYear()}-01-01`,$MONTH_AGO:()=>{let t=new Date;return t.setMonth(t.getMonth()-1),pn(t)}};function pn(t){return `${t.getFullYear()}-${mn(t.getMonth()+1)}-${mn(t.getDate())}`}function mn(t){return t.toString().padStart(2,"0")}function _a(t){if(typeof t!="string")return t;let e=Ra[t];return e?e():t}function Hr(t){return {...t,queryTemplates:t.queryTemplates.map(e=>({...e,filters:e.filters.map(n=>({...n,value:_a(n.value)}))}))}}function jr(t){let e=new ResourceTemplate("sap://routing/{domain}",{list:void 0});t.registerResource("sap-routing",e,{title:"SAP Domain Routing",description:"Service mappings and query templates for a business domain. Returns the relevant SAP services, entity sets, and pre-built query examples for a domain like business-partner, sales, or finance. Use this before building OData queries to know which service and entity set to target.",mimeType:"application/json"},async(n,r)=>{let s=r.domain,i=un[s];if(!i)return {contents:[{uri:n.toString(),mimeType:"application/json",text:JSON.stringify({error:`Unknown domain '${s}'`,availableDomains:Object.keys(un)},null,2)}]};let o=Hr(i);return {contents:[{uri:n.toString(),mimeType:"application/json",text:JSON.stringify({...o,note:"Service paths are typical defaults. Use sap_discover_services to verify actual paths on your system."},null,2)}]}});}var fn={core:["test-connection","sap_list_services","sap_get_metadata","sap_discover_services","sap_nl_query"],odata:["sap_read","sap_query","sap_create","sap_update","sap_delete","sap_function","sap_batch"],idoc:["sap_idoc_send","sap_idoc_status","sap_idoc_discover","sap_idoc_list_received"],rfc:["sap_rfc_call","sap_rfc_metadata","sap_bapi_call","sap_bapi_commit","sap_rfc_search_functions"]},Vr=new Set(["sap_create","sap_update","sap_delete","sap_function","sap_batch","sap_idoc_send","sap_rfc_call","sap_bapi_call","sap_bapi_commit"]),Wr=["core","odata"],qr=new Map;for(let[t,e]of Object.entries(fn))for(let n of e)qr.set(n,t);var wt=class{handles=new Map;activeTiers;options;constructor(e){this.options=e,this.activeTiers=new Set(e.activeTiers);}registerHandle(e,n){this.handles.set(e,n);}applyInitialVisibility(){for(let[e,n]of this.handles)this.shouldToolBeVisible(e)||n.disable();}enableTier(e){if(this.options.allowedTiers&&!this.options.allowedTiers.includes(e))return {error:`Cannot enable ${e} tier: not permitted by the token policy of this connection (allowed: ${this.options.allowedTiers.join(", ")})`};if(e==="idoc"&&!this.options.hasIdocConfig)return {error:"Cannot enable IDoc tier: no IDoc partner configuration provided (--sndprn, --rcvprn required)"};if(e==="rfc"&&!this.options.hasRfcConfig)return {error:"Cannot enable RFC tier: no RFC connection configuration provided (SAP_RFC_ASHOST + SAP_RFC_SYSNR, or SAP_RFC_MSHOST + SAP_RFC_GROUP required)"};this.activeTiers.add(e);let n=fn[e],r=[];for(let s of n){if(this.options.readOnly&&Vr.has(s))continue;let i=this.handles.get(s);i&&(i.enable(),r.push(s));}return {enabled:r,activeTiers:this.getActiveTiers()}}getActiveTiers(){return Array.from(this.activeTiers)}getAllowedTiers(){return this.options.allowedTiers?[...this.options.allowedTiers]:void 0}getToolsByTier(e){return fn[e]}isReadOnlyMode(){return this.options.readOnly}shouldToolBeVisible(e){let n=qr.get(e);return n?!(!this.activeTiers.has(n)||this.options.allowedTiers&&!this.options.allowedTiers.includes(n)||n==="idoc"&&!this.options.hasIdocConfig||n==="rfc"&&!this.options.hasRfcConfig||this.options.readOnly&&Vr.has(e)):true}};var Kr="/sap/opu/odata/sap",Oa=/^[A-Z0-9_/]+$/i,oe=class extends Error{guidance;constructor(e){super(`serviceUrl ist weder ein Pfad noch ein technischer Servicename: "${e}"`),this.name="InvalidServiceUrlError",this.guidance=`Erwartet wird ein Pfad wie ${Kr}/API_BUSINESS_PARTNER oder der technische Name (API_BUSINESS_PARTNER), den sap_discover_services als technicalName liefert.`;}};function H(t){let e=t.trim();if(e.length===0)throw new oe(t);if(e.startsWith("/"))return e.replace(/\/+$/,"")||"/";if(e.startsWith("http://")||e.startsWith("https://"))try{return new URL(e).pathname.replace(/\/+$/,"")}catch{throw new oe(t)}if(Oa.test(e))return `${Kr}/${e.replace(/^\/+|\/+$/g,"")}`;throw new oe(t)}var Fe=class extends Error{guidance;extra;constructor(e,n,r={}){super(e),this.name="ToolInputError",this.guidance=n,this.extra=r;}};function N(t,e){if(t instanceof oe)return gn({code:"INVALID_INPUT",error:t.message,guidance:t.guidance,...e?.hint?{hint:e.hint}:{}});if(t instanceof Fe)return gn({code:"INVALID_INPUT",error:t.message,...t.guidance?{guidance:t.guidance}:{},...t.extra,...e?.hint?{hint:e.hint}:{}});let n=pe(t,{serviceUrl:e?.serviceUrl});return gn({code:n.code,error:n.message,...n.httpStatus!==void 0?{httpStatus:n.httpStatus}:{},...n.sapCode?{sapCode:n.sapCode}:{},...n.guidance?{guidance:n.guidance}:{},...e?.hint?{hint:e.hint}:{}})}function gn(t){let e=le()?.correlationId;return {content:[{type:"text",text:JSON.stringify({...t,...e?{correlationId:e}:{}},null,2)}],isError:true}}function Gr(t,e,n){let r=n??new k,s=new Map;function i(a){let c=H(a),l=s.get(c);return l||(l=new M(e,c,r),s.set(c,l)),l}function o(a,c){return N(a,{serviceUrl:c})}t.registerTool("sap_batch",{title:"Execute SAP Batch Request",description:"Execute multiple OData operations in a single batch request. Supports V2 (multipart/mixed) and V4 (JSON) batch formats automatically. Operations can be grouped into changesets for transactional behavior (all succeed or all fail). Returns per-operation status -- individual failures are always reported, never hidden behind the batch HTTP 200.",inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),operations:z$2.array(z$2.object({method:z$2.enum(["get","post","patch","delete"]).describe("HTTP method"),entitySet:z$2.string().describe("Entity set name"),key:z$2.record(z$2.string(),z$2.unknown()).optional().describe("Entity key for read/update/delete"),data:z$2.record(z$2.string(),z$2.unknown()).optional().describe("Request body for create/update"),changesetId:z$2.string().optional().describe("Changeset group ID for transactional grouping")})).describe("Array of operations to execute in batch")}),annotations:{readOnlyHint:false,destructiveHint:true,idempotentHint:false,openWorldHint:true}},async({serviceUrl:a,operations:c})=>{try{let l=i(a),d=c,u=await l.batch(d);return {content:[{type:"text",text:JSON.stringify(u,null,2)}]}}catch(l){return o(l)}});}async function zr(t,e,n=5){if(t.length===0)return [];let r=Math.max(1,Math.min(Math.floor(n)||1,t.length)),s=new Array(t.length),i=0;async function o(){for(;;){let a=i++;if(a>=t.length)return;s[a]=await e(t[a],a);}}return await Promise.all(Array.from({length:r},()=>o())),s}var Ia=[".integrationsuite.","integrationsuite-trial.",".it-cpi.",".hci.",".prod.apimanagement.","-api.s4hana.ondemand.com"];function Yr(t){let e=t.toLowerCase();return Ia.some(n=>e.includes(n))}var V=class extends Error{constructor(n,r,s,i){super(n);this.httpStatus=r;this.sapGuidance=s;this.isBtpDetected=i;this.name="CatalogError";}},Da=new Set(["ECONNREFUSED","ETIMEDOUT","ENOTFOUND"]);function Na(t,e=false){switch(t){case 403:return "Catalog access denied. Check transaction PFCG for role assignment and SU53 for authorization trace. Required authorization object: S_SERVICE with SRV_NAME = IWFND_CATALOGSERVICE.";case 404:return e?"V4 catalog not available. Publish service group via /IWFND/V4_ADMIN.":"Catalog service not found. Activate it via transaction /IWFND/MAINT_SERVICE.";case 500:return "SAP system error. Check transaction ST22 for ABAP dumps and SM21 for system log.";default:return "Could not connect to SAP system. Verify the hostname, port, and network connectivity."}}function xa(t){switch(t){case "ECONNREFUSED":return "Connection refused. Verify that the SAP system is running and the port is correct.";case "ETIMEDOUT":return "Connection timeout. Check network connectivity and firewall rules.";case "ENOTFOUND":return "Hostname not found. Verify the SAP system hostname in your configuration.";default:return "Could not connect to SAP system. Verify the hostname, port, and network connectivity."}}function Xr(t){return typeof t=="object"&&t!==null&&"response"in t&&typeof t.response=="object"&&t.response!==null&&typeof t.response.status=="number"}function Ma(t){return typeof t=="object"&&t!==null&&"code"in t&&typeof t.code=="string"&&Da.has(t.code)}function He(t,e,n=false){let r=Yr(e);if(Xr(t)){let i=t.response.status,o=Na(i,n),a="message"in t&&typeof t.message=="string"?t.message:`HTTP ${i}`;return new V(a,i,o,r)}if(Ma(t)){let i=xa(t.code),o="message"in t&&typeof t.message=="string"?t.message:t.code;return new V(o,void 0,i,r)}let s=t instanceof Error?t.message:String(t);return new V(s,void 0,void 0,r)}function Jr(t){return Xr(t)&&t.response.status===404?"V4 catalog not available. Publish service group via /IWFND/V4_ADMIN.":t instanceof Error?t.message:String(t)}var Ua=["/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/iwfnd/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/IWFND/CATALOGSERVICE/ServiceCollection"];function $a(t){if(typeof t=="object"&&t!==null&&"response"in t)return t.response?.status}async function Qr(t,e,n){let r;for(let s of Ua)try{return await t.request({method:"get",url:s,params:{$format:"json",$top:"1"},expectedStatuses:[404]}),n?.onMatch?.(s),s}catch(i){if(i instanceof $)throw i;if($a(i)===404){r=i;continue}throw He(i,e,false)}throw He(r??new Error("All V2 catalog paths returned 404"),e,false)}function Zr(t){try{return new URL(t).pathname}catch{return t}}function Ba(t){let e=t.replace(/'/g,"''");return `substringof('${e}',TechnicalServiceName) or substringof('${e}',Title)`}var Et=class t{constructor(e,n,r={}){this.httpClient=e;this.cache=n;this.options=r;}static DEFAULT_V4_URL="/sap/opu/odata4/iwfnd/config/default/iwfnd/catalog/0002/ServiceGroups";resolvedV2Path;async discoverServices(e){let n=this.cache.get();if(n)return {services:n,warnings:[]};if(e){let l=await this.tryFilteredFetch(e);if(l)return l}let r=[],[s,i]=await zr(["v4","v2"],async l=>l==="v4"?await this.fetchV4Catalog().then(d=>({ok:true,services:d}),d=>({ok:false,error:d})):await this.fetchV2Catalog().then(d=>({ok:true,services:d}),d=>({ok:false,error:d})),this.options.concurrency??5),o=[];if(s?.ok)o=s.services;else if(s){if(s.error instanceof $)throw s.error;r.push(`V4 catalog fetch failed: ${Jr(s.error)}`);}if(!i?.ok){let l=i?.error;throw l instanceof V?l:He(l,this.options.baseUrl??"",false)}let a=i.services,c=this.mergeAndDeduplicate(o,a);return this.cache.set(c),{services:c,warnings:r}}async tryFilteredFetch(e){try{return {services:await this.fetchV2Catalog(Ba(e)),warnings:[],filtered:!0}}catch(n){if(n instanceof $)throw n;n?.response?.status;return void 0}}async fetchV2Catalog(e){let n=this.options.catalogV2Url??this.resolvedV2Path;return n||(n=await Qr(this.httpClient,this.options.baseUrl??"",{onMatch:this.options.onDiscoveryMatch}),this.resolvedV2Path=n),((await this.httpClient.request({method:"get",url:n,params:{$format:"json",...e?{$filter:e}:{}},...e?{expectedStatuses:[400,501]}:{}})).data?.d?.results??[]).map(o=>({technicalName:o.TechnicalServiceName,title:o.Title,serviceUrl:Zr(o.ServiceUrl),description:o.Description,version:"v2",serviceVersion:o.TechnicalServiceVersion}))}async fetchV4Catalog(){let e=this.options.catalogV4Url??t.DEFAULT_V4_URL,s=(await this.httpClient.request({method:"get",url:e,params:{$expand:"DefaultSystem($expand=Services)"},headers:{Accept:"application/json"},expectedStatuses:[404]})).data?.value??[],i=[];for(let o of s){let a=o.DefaultSystem?.Services??[];for(let c of a)i.push({technicalName:c.ServiceId??c.ServiceName??"",title:c.ServiceTitle??c.Title??"",serviceUrl:Zr(c.ServiceUrl),version:"v4",serviceVersion:c.ServiceVersion});}return i}mergeAndDeduplicate(e,n){let r=new Map;for(let s of e)r.set(s.technicalName,s);for(let s of n)r.has(s.technicalName)||r.set(s.technicalName,s);return [...r.values()]}};var Fa=[[/BUSINESS_?PARTNER|BUPA/i,"business-partner"],[/SALES_ORDER|BILLING|CREDIT_MEMO|DEBIT_MEMO|SALES_CONTRACT|SALES_QUOT/i,"sales"],[/PURCHASEORDER|PURCHASE_ORDER|PURCHASE_REQ|SUPPLIER|PURG/i,"procurement"],[/JOURNALENTRY|JOURNAL_ENTRY|OPLACCTG|COST_CENTER|PROFIT_CENTER|GL_ACCOUNT|FINANCIAL/i,"finance"],[/MATERIAL_DOC|MATERIAL_STOCK|MATERIAL_SRV|PRODUCT\b/i,"material"],[/OUTBOUND_DELIV|INBOUND_DELIV|SHIPPING|TRANSPORTATION/i,"logistics"],[/PRODUCTION_ORDER|PLANNED_ORDER|ROUTING|BOM\b/i,"production"],[/EMPLOYEE|WORKFORCE|PERSONNEL|PAYROLL/i,"hr"],[/INSPECTION|QUALITY/i,"quality"],[/MAINTENANCE|EQUIPMENT|FUNCTIONAL_LOC/i,"plant-maintenance"],[/PROJECT|WBS\b/i,"project"],[/WAREHOUSE|STORAGE_BIN/i,"warehouse"],[/ANALYTIC|CUBE/i,"analytics"]],Ha=[[/^[ZY]?MM[_]/i,"procurement"],[/^[ZY]?SD[_]/i,"sales"],[/^[ZY]?FI[_]|^[ZY]?CO[_]/i,"finance"],[/^[ZY]?PP[_]/i,"production"],[/^[ZY]?HR[_]|^[ZY]?PA[_]|^[ZY]?PT[_]/i,"hr"],[/^[ZY]?WM[_]/i,"warehouse"],[/^[ZY]?PM[_]/i,"plant-maintenance"],[/^[ZY]?LE[_]/i,"logistics"]];function ja(t){for(let[e,n]of Fa)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};for(let[e,n]of Ha)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};return {...t,category:"unknown",categoryConfidence:"low"}}function es(t){return t.map(ja)}function Ee(t,e){let n=Number.isFinite(t)&&t>0?Math.floor(t):50,r=Number.isFinite(e)&&e>0?Math.floor(e):0;return {top:Math.min(n,1e3),skip:r}}function Ce(t){let{returned:e,totalCount:n,paging:r,hasMoreHint:s}=t,i=s??(n!==void 0?r.skip+e<n:e>=r.top),o={returned:e,hasMore:i,top:r.top,skip:r.skip};return n!==void 0&&(o.totalCount=n),i&&(o.nextSkip=r.skip+e),o}function ts(t,e,n,r){let s=r??new G,i=new Et(e,s,n);t.registerTool("sap_discover_services",{title:"Discover SAP OData Services",description:`Discover all available OData services on the connected SAP system. Returns categorized services with search, category filtering, and pagination. Use this as the first step to explore what APIs are available before using sap_list_services or sap_get_metadata.
|
|
212
212
|
|
|
213
213
|
**When to use:**
|
|
214
214
|
- You need to find which SAP services are available on this system
|
|
@@ -323,7 +323,7 @@ When NOT to use:
|
|
|
323
323
|
Wildcards (SAP-native, NOT glob):
|
|
324
324
|
- \`*\` matches any sequence of characters
|
|
325
325
|
- \`+\` matches a single character
|
|
326
|
-
Patterns are case-folded to uppercase. Default limit is 100; set higher to widen, but SAP returns unpaginated results \u2014 large patterns may stress the backend.`,inputSchema:z$2.object({pattern:z$2.string().min(1).describe('Name pattern with SAP wildcards (* and +). Examples: "BAPI_USER_*", "Z_*", "+MATERIAL+"'),groupName:z$2.string().optional().describe('Optional function group filter (RS38L_AREA, e.g. "SU05")'),limit:z$2.number().int().positive().optional().default(100).describe("Max number of hits to return (default 100). Excess is truncated.")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({pattern:r,groupName:s,limit:i})=>{try{let o={FUNCNAME:r.toUpperCase()};s&&(o.GROUPNAME=s.toUpperCase());let c=(await e.callOnce("RFC_FUNCTION_SEARCH",o)).FUNCTIONS??[],l=c.length,d=i??100,u=l>d,f={functions:(u?c.slice(0,d):c).map(g=>{let h=String(g.TEXT??"").trim();return {name:String(g.FUNCNAME??"").trim(),groupName:String(g.GROUPNAME??"").trim(),shortText:h===""?void 0:h}}),total:l,...u?{truncated:!0}:{}};return {content:[{type:"text",text:JSON.stringify(f,null,2)}]}}catch(o){return qe(o)}});}function Ke(t,e){let n=new McpServer({name:wr,version:br},{capabilities:{logging:{},resources:{},prompts:{},tools:{listChanged:true}}}),r={readOnly:e?.readOnly??false,activeTiers:e?.tiers??Wr,...e?.allowedTiers?{allowedTiers:e.allowedTiers}:{},hasIdocConfig:e?.hasIdocConfig??false,hasRfcConfig:e?.hasRfcConfig??false},s=new vt(r),i=n.registerTool.bind(n),o=e?.toolTimeoutMs??ht*1e3;n.registerTool=((h,...S)=>{let T=S.length-1,y=S[T];typeof y=="function"&&(S[T]=Sr(h,y,{toolTimeoutMs:o,logger:e?.logger,tlsVerifyDisabled:e?.httpOptions?.tlsVerify===false}));let b=i(h,...S);return s.registerHandle(h,b),b});let a={baseUrl:t.baseUrl,...e?.cacheDir?{persistDir:e.cacheDir}:{},...e?.logger?{onWarn:(h,S)=>e.logger?.warn({err:S},h)}:{}},c=e?.caches?.catalog??new G(a),l=e?.caches?.metadata??new k(a),d=Ye(t),u=new fe(d,e?.httpOptions);e?.userContext&&(u.userContext=e.userContext);let m=e?.exposePatterns,p=e?.sapClient??t.sapClient;Er(n),vr(n),ss(n,u,{baseUrl:t.baseUrl,sapClient:p,tlsVerify:e?.httpOptions?.tlsVerify}),ns(n,u,m),ts(n,u,{catalogV2Url:e?.catalogV2Url,catalogV4Url:e?.catalogV4Url,baseUrl:t.baseUrl,onDiscoveryMatch:e?.onDiscoveryMatch,...e?.concurrency?{concurrency:e.concurrency}:{}},c),ps(n,u,m,l),rs(n,u,l),Gr(n,u,l),us(n,u,m,l);let f=e?.idocStore??new we,g=new gt(u,p,e?.partnerDefaults);return as(n,u,g,f),Br(n,f),e?.rfcAvailable&&e.rfcPool&&e.logger&&ys(n,e.rfcPool,e.logger),ds(n,s),Fr(n,u,l),Lr(n,u,l),jr(n),s.applyInitialVisibility(),{server:n,registry:s,httpClient:u}}var uc=z$1.object({snc_qop:z$1.enum(["1","2","3","8","9"]).optional(),snc_myname:z$1.string().min(1).optional(),snc_partnername:z$1.string().min(1).optional(),snc_lib:z$1.string().min(1).optional()}).superRefine((t,e)=>{let n=[t.snc_qop,t.snc_myname,t.snc_partnername,t.snc_lib].filter(r=>r!==void 0).length;if(n>0&&n<4){let r=[];t.snc_qop||r.push("SAP_SNC_QOP"),t.snc_myname||r.push("SAP_SNC_MYNAME"),t.snc_partnername||r.push("SAP_SNC_PARTNERNAME"),t.snc_lib||r.push("SAP_SNC_LIB"),e.addIssue({code:"custom",message:`SNC config incomplete: missing ${r.join(", ")} (D-12 all-or-none)`,path:["snc_qop"]});}});function Sn(t=process.env){let e=t.SAP_USERNAME,n=t.SAP_PASSWORD;if(!e||!n)return null;let r=!!(t.SAP_RFC_ASHOST&&t.SAP_RFC_SYSNR),s=!!(t.SAP_RFC_MSHOST&&t.SAP_RFC_GROUP);if(!r&&!s)return null;let i={user:e,passwd:n,client:t.SAP_CLIENT,lang:t.SAP_RFC_LANG??"EN"};r&&(i.ashost=t.SAP_RFC_ASHOST,i.sysnr=t.SAP_RFC_SYSNR),s&&(i.mshost=t.SAP_RFC_MSHOST,i.msserv=t.SAP_RFC_MSSERV,i.group=t.SAP_RFC_GROUP);let o=uc.safeParse({snc_qop:t.SAP_SNC_QOP,snc_myname:t.SAP_SNC_MYNAME,snc_partnername:t.SAP_SNC_PARTNERNAME,snc_lib:t.SAP_SNC_LIB});if(!o.success){let a=o.error.issues.map(c=>c.message).join("; ");throw new Error(`Invalid SNC configuration: ${a}`)}return o.data.snc_qop&&Object.assign(i,o.data),i}function Ss(t=process.env){try{return Sn(t)!==null}catch{return false}}var _t=class{constructor(e={}){this.options=e;this.discovery=e.discoveryClient??new ee;}discovery;jwks;jwksIssuer;get verifies(){return !!this.options.issuerUrl}async verify(e){let n=e.authorization??e.Authorization;if(typeof n!="string"||!n.startsWith("Bearer "))return {kind:"none"};let r=n.slice(7).trim();if(!r)return {kind:"none"};if(!this.options.issuerUrl)return this.withoutVerification(r);try{let s=await this.getKeySet(this.options.issuerUrl),{payload:i}=await jwtVerify(r,s,{issuer:void 0,...this.options.audience?{audience:this.options.audience}:{}});return typeof i.sub!="string"?{kind:"rejected",reason:"Token ohne sub-Claim"}:{kind:"user",context:{token:r,sub:i.sub,email:typeof i.email=="string"?i.email:void 0,name:typeof i.name=="string"?i.name:void 0,verified:!0}}}catch(s){return gc(r)?{kind:"rejected",reason:s instanceof Error?s.message:"Signaturpr\xFCfung fehlgeschlagen"}:{kind:"none"}}}withoutVerification(e){try{let n=decodeJwt(e);return typeof n.sub!="string"?{kind:"none"}:{kind:"user",context:{token:e,sub:n.sub,email:typeof n.email=="string"?n.email:void 0,name:typeof n.name=="string"?n.name:void 0,verified:!1}}}catch{return {kind:"none"}}}async getKeySet(e){if(this.jwks&&this.jwksIssuer===e)return this.jwks;let n=await this.discovery.discover(e);return this.jwks=createRemoteJWKSet(new URL(n.jwks_uri)),this.jwksIssuer=e,this.jwks}};function gc(t){let e=t.split(".");return e.length===3&&e.every(n=>n.length>0)}var hc="x-sap-username",yc="x-sap-password",Sc="x-sap-authorization";function Tn(t,e){let n=t[e]??t[e.toLowerCase()],r=Array.isArray(n)?n[0]:n;return typeof r=="string"&&r.length>0?r:void 0}function Tc(t){let e=Tn(t,hc),n=Tn(t,yc);if(e&&n)return {username:e,password:n};let r=Tn(t,Sc),s=r?/^Basic\s+(.+)$/i.exec(r.trim()):null;if(s?.[1]){let i=Buffer.from(s[1].trim(),"base64").toString("utf8"),o=i.indexOf(":");if(o>0&&o<i.length-1)return {username:i.slice(0,o),password:i.slice(o+1)}}}function An(t,e){let n=Tc(e);if(t.authType==="user-basic"){if(!n)return {kind:"missing",hint:`Destination "${t.name}" verlangt eine pers\xF6nliche SAP-Anmeldung: sende X-SAP-Username und X-SAP-Password (oder X-SAP-Authorization: Basic <base64 user:pass>) mit \u2014 im n8n-Node \u201EGuniWeb SAP" sind das die Felder SAP-Benutzer/SAP-Passwort des Credentials.`};let{name:r,baseUrl:s,sapClient:i}=t;return {kind:"ok",config:{name:r,baseUrl:s,authType:"basic",...i!==void 0?{sapClient:i}:{},username:n.username,password:n.password},username:n.username}}return n?{kind:"rejected",reason:`Destination "${t.name}" (authType ${t.authType}) nimmt keine pers\xF6nliche SAP-Anmeldung an \u2014 die Anfrage w\xFCrde unter dem konfigurierten technischen Zugang laufen. Lass X-SAP-Username/X-SAP-Password weg oder stelle die Destination auf authType user-basic um.`}:{kind:"ok",config:t}}var Ac=z$2.object({destination:z$2.string().min(1),label:z$2.string().max(200).optional(),readOnly:z$2.boolean().optional(),tiers:z$2.array(z$2.string()).optional()}).strict();function ae(t,e,n){t.setHeader("Cache-Control","no-store"),t.status(e).json(n);}function Ts(t){return t.ip??t.socket?.remoteAddress??"unknown"}function As(t,e){let n=t?.[e];return n==="true"||n==="1"||n===""}function bs(t){t.use("/admin",(e,n)=>{ae(n,404,{error:"Admin API not enabled",hint:"Start the server with --admin-token <secret> (or SAP_MCP_ADMIN_TOKEN) and --destinations <file>."});});}function ws(t,e){let{destinations:n,logger:r}=e,s=new Se({apiKey:e.adminToken,tokens:()=>[]});t.use("/admin",(c,l,d)=>{let u=c,m=l,p=Ts(u),f=s.authenticate(u.headers.authorization,p);if(f.kind==="rate-limited"){r?.warn({clientAddress:p,path:u.originalUrl??u.path,retryAfterSeconds:f.retryAfterSeconds},"Admin-API: zu viele fehlgeschlagene Anmeldeversuche von dieser Adresse"),m.setHeader("Retry-After",String(f.retryAfterSeconds)),ae(m,429,{error:"Too Many Requests",retryAfterSeconds:f.retryAfterSeconds});return}if(f.kind!=="api-key"){r?.warn({clientAddress:p,path:u.originalUrl??u.path},"Admin-API: Anfrage abgewiesen"),ae(m,401,{error:"Unauthorized"});return}d();});let i=()=>{if(n.source!=="file"||!n.filePath)throw new P("Admin API requires a destinations file: start the server with --destinations <file>.",409);return new Te({filePath:n.filePath,env:e.env})},o=(c,l)=>(d,u)=>{let m=d,p=u,f=Ts(m);(async()=>{try{let{status:g,body:h,changed:S}=l(m);if(S){let T=await n.reload();h.reloaded=T.ok,T.ok||(h.reloadErrors=T.errors),r?.info({action:c,clientAddress:f,target:m.params?.name??m.params?.ref??m.body?.destination,reloaded:T.ok},"Admin-API: \xC4nderung ausgef\xFChrt");}ae(p,g,h);}catch(g){if(g instanceof P){ae(p,g.status,{error:g.message});return}r?.warn({action:c,clientAddress:f,err:g instanceof Error?g.message:String(g)},"Admin-API: unerwarteter Fehler"),ae(p,500,{error:"Internal Server Error"});}})();},a=c=>{let l=c.body;if(l===null||typeof l!="object"||Array.isArray(l))throw new P("Request body must be a JSON object (Content-Type: application/json).",400);return l};t.get("/admin/destinations",o("destinations.list",()=>({status:200,body:{destinations:i().listDestinations()},changed:!1}))),t.put("/admin/destinations/:name",o("destinations.put",c=>{let l=c.params?.name??"",d=a(c),{replaced:u,warnings:m}=i().addDestination(l,d,{replace:!0,allowMissingEnv:As(c.query,"allowMissingEnv")});return {status:u?200:201,body:{name:l,replaced:u,warnings:m},changed:!0}})),t.delete("/admin/destinations/:name",o("destinations.delete",c=>{let l=c.params?.name??"",{removedTokens:d}=i().removeDestination(l,{force:As(c.query,"force")});return {status:200,body:{name:l,removedTokens:d},changed:!0}})),t.get("/admin/tokens",o("tokens.list",()=>({status:200,body:{tokens:i().listTokens()},changed:!1}))),t.post("/admin/tokens",o("tokens.issue",c=>{let l=Ac.safeParse(a(c));if(!l.success)throw new P(`Invalid body: ${l.error.issues.map(g=>`${g.path.join(".")||"(root)"}: ${g.message}`).join("; ")}`,400);let{destination:d,...u}=l.data,{token:m,hash:p,entry:f}=i().issueToken(d,u);return {status:201,body:{token:m,hash:p.replace(/^sha256:/,"").slice(0,12),destination:f.destination,label:f.label,policy:f.policy,createdAt:f.createdAt},changed:!0}})),t.delete("/admin/tokens/:ref",o("tokens.revoke",c=>{let l=c.params?.ref??"",d=i().revokeToken(l);return {status:200,body:{...d,revoked:!0},changed:!d.alreadyRevoked}})),t.use("/admin",(c,l)=>{ae(l,404,{error:"Not Found"});});}function vs(t,e){return e?.kind==="token"?t.destinations?.get(e.token.destination):t.destinations?t.destinations.defaultDestination():t.config}function Es(t,e){if(e?.kind!=="token"||!e.token.policy)return t;let n=e.token.policy;return {...t,readOnly:t.readOnly===true||n.readOnly===true,...n.tiers?{allowedTiers:[...n.tiers]}:{}}}function Cs(t,e){t.writeHead(503,{"Content-Type":"application/json"});let n=e?.names().length??0;t.end(JSON.stringify({error:"Keine Destination bestimmbar",hint:n===0?'Es ist noch keine Destination konfiguriert. Lege eine an \u2014 per Admin-API (PUT /admin/destinations/<name>) oder CLI (guniweb-sap-mcp destinations add) \u2014 und ein Token daf\xFCr, oder nenne sie "default".':'Es sind mehrere Destinations konfiguriert, aber keine hei\xDFt "default". Benenne eine Destination "default", lege Tokens an oder reduziere die Datei auf ein Ziel.'}));}var bn=class t{constructor(e){this.persistDir=e;}entries=new Map;static MAX_USER_SCOPES=100;for(e,n){let r=`${e.name}\0${e.baseUrl}\0${n??""}`,s=this.entries.get(r);if(!s){for(let o of this.entries.keys())o.startsWith(`${e.name}\0`)&&!o.startsWith(`${e.name}\0${e.baseUrl}\0`)&&this.entries.delete(o);n!==void 0&&this.evictOldestUserScopes(e);let i={baseUrl:e.baseUrl,...this.persistDir&&n===void 0?{persistDir:this.persistDir}:{}};s={catalog:new G(i),metadata:new k(i)},this.entries.set(r,s);}return s}evictOldestUserScopes(e){let n=`${e.name}\0${e.baseUrl}\0`,r=[...this.entries.keys()].filter(s=>s.startsWith(n)&&s!==n);for(;r.length>=t.MAX_USER_SCOPES;)this.entries.delete(r.shift());}};function Ps(t,e){if(e.kind==="missing"){t.writeHead(401,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"SAP login required",hint:e.hint}));return}t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:e.reason}));}function Rs(t,e){let n=t.logger;return !e||!n||typeof n.child!="function"?t:{...t,logger:n.child({sapUser:e})}}async function _s(t){let e=new _t({...t.jwtIssuer?{issuerUrl:t.jwtIssuer}:{},...t.jwtAudience?{audience:t.jwtAudience}:{}}),n=createMcpExpressApp({host:"0.0.0.0"}),r=new Se({apiKey:t.apiKey,tokens:()=>t.destinations?.tokens()??[]}),s=t.serverOptions?.logger;n.use((a,c,l)=>{let d=a,u=c,m=l;if(d.path==="/healthz"){m();return}if(d.path==="/admin"||d.path.startsWith("/admin/")){m();return}if(t.webhookSecret&&d.path==="/idoc"){m();return}let p=d.ip??d.socket?.remoteAddress??"unknown",f=r.authenticate(d.headers.authorization,p);if(f.kind==="rate-limited"){s?.warn({clientAddress:p,path:d.path,retryAfterSeconds:f.retryAfterSeconds},"MCP-Anfrage abgewiesen: zu viele fehlgeschlagene Anmeldeversuche von dieser Adresse"),u.setHeader("Retry-After",String(f.retryAfterSeconds)),u.status(429).json({error:"Too Many Requests",retryAfterSeconds:f.retryAfterSeconds});return}if(f.kind==="rejected"){s?.warn({clientAddress:p,path:d.path,reason:f.reason},"MCP-Anfrage abgewiesen"),u.status(401).json({error:"Unauthorized"});return}d.sapAuth=f,m();}),n.get("/healthz",(a,c)=>{c.status(200).json({status:"ok"});}),t.adminToken&&t.destinations?ws(n,{adminToken:t.adminToken,destinations:t.destinations,env:t.env??process.env,logger:s}):bs(n),t.transport==="http"?Cc(n,t,e):Pc(n,t,e);let i=pt(t.idocStore,{secret:t.webhookSecret,maxBodyBytes:t.webhookMaxBodyBytes});n.post("/idoc",(a,c)=>{i(a,c);}),n.use((a,c,l,d)=>{let u=a,m=l;if(u?.type==="entity.parse.failed"&&!m.headersSent){m.status(400).json({error:`Invalid JSON body: ${u.message??"parse error"}`});return}d(a);});let o=createServer(n);return new Promise(a=>{o.listen(t.port,()=>{let c=o.address(),l=typeof c=="object"&&c!==null?c.port:t.port;a({port:l,close:()=>new Promise((d,u)=>{o.close(m=>m?u(m):d());})});});})}function Cc(t,e,n){let r=new bn(e.serverOptions?.cacheDir),s=e.serverOptions?.logger;t.all("/mcp",(i,o)=>{let a=i,c=o,l=vs(e,a.sapAuth);if(!l){Cs(c,e.destinations);return}let d=An(l,a.headers);if(d.kind!=="ok"){s?.warn({destination:l.name,clientAddress:a.socket?.remoteAddress,reason:d.kind},d.kind==="missing"?"MCP-Anfrage ohne pers\xF6nliche SAP-Anmeldung an eine user-basic-Destination abgewiesen":"MCP-Anfrage mit pers\xF6nlicher SAP-Anmeldung an eine Destination mit technischem Benutzer abgewiesen"),Ps(c,d);return}let u=d.config,m=d.username,p=new StreamableHTTPServerTransport({sessionIdGenerator:void 0}),f=g=>{let{server:h}=Ke(u,{...Es(Rs(e.serverOptions,m),a.sapAuth),sapClient:u.sapClient,userContext:g,caches:r.for(u,m)});h.connect(p).then(()=>p.handleRequest(a,c,a.body)).catch(()=>{c.headersSent||(c.writeHead(500),c.end("Internal Server Error"));});};n.verify(a.headers).then(g=>{if(g.kind==="rejected"){c.writeHead(401,{"Content-Type":"application/json"}),c.end(JSON.stringify({error:"Unauthorized",reason:g.reason,hint:"Das mitgeschickte Bearer-Token hat die Signaturpr\xFCfung gegen SAP_JWT_ISSUER nicht bestanden."}));return}let h=g.kind==="user"?g.context:null;f(h);});});}function Pc(t,e,n){let r=new Map;t.get("/sse",(s,i)=>{let o=s,a=i,c=vs(e,o.sapAuth);if(!c){Cs(a,e.destinations);return}let l=An(c,o.headers??{});if(l.kind!=="ok"){Ps(a,l);return}let d=l.config,u=new SSEServerTransport("/message",a),{server:m,httpClient:p}=Ke(d,{...Es(Rs(e.serverOptions,l.username),o.sapAuth),sapClient:d.sapClient});m.connect(u).then(()=>u.start()).then(()=>{r.set(u.sessionId,{transport:u,server:m,httpClient:p}),u.onclose=()=>{r.delete(u.sessionId);};}).catch(()=>{a.headersSent||(a.writeHead(500),a.end("Internal Server Error"));});}),t.post("/message",(s,i)=>{let o=s,a=i,l=new URL(o.url??"",`http://${o.headers.host??"localhost"}`).searchParams.get("sessionId")??o.query?.sessionId;if(!l||!r.has(l)){a.writeHead(400,{"Content-Type":"application/json"}),a.end(JSON.stringify({error:"Invalid or missing session ID"}));return}let d=r.get(l);n.verify(o.headers).then(u=>{if(u.kind==="rejected"){a.writeHead(401,{"Content-Type":"application/json"}),a.end(JSON.stringify({error:"Unauthorized",reason:u.reason}));return}d.httpClient.userContext=u.kind==="user"?u.context:null,d.transport.handlePostMessage(o,a,o.body).catch(()=>{a.headersSent||(a.writeHead(500),a.end("Internal Server Error"));});});});}var Oc=`Usage: guniweb-sap-mcp [options]
|
|
326
|
+
Patterns are case-folded to uppercase. Default limit is 100; set higher to widen, but SAP returns unpaginated results \u2014 large patterns may stress the backend.`,inputSchema:z$2.object({pattern:z$2.string().min(1).describe('Name pattern with SAP wildcards (* and +). Examples: "BAPI_USER_*", "Z_*", "+MATERIAL+"'),groupName:z$2.string().optional().describe('Optional function group filter (RS38L_AREA, e.g. "SU05")'),limit:z$2.number().int().positive().optional().default(100).describe("Max number of hits to return (default 100). Excess is truncated.")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({pattern:r,groupName:s,limit:i})=>{try{let o={FUNCNAME:r.toUpperCase()};s&&(o.GROUPNAME=s.toUpperCase());let c=(await e.callOnce("RFC_FUNCTION_SEARCH",o)).FUNCTIONS??[],l=c.length,d=i??100,u=l>d,f={functions:(u?c.slice(0,d):c).map(g=>{let h=String(g.TEXT??"").trim();return {name:String(g.FUNCNAME??"").trim(),groupName:String(g.GROUPNAME??"").trim(),shortText:h===""?void 0:h}}),total:l,...u?{truncated:!0}:{}};return {content:[{type:"text",text:JSON.stringify(f,null,2)}]}}catch(o){return qe(o)}});}function Ke(t,e){let n=new McpServer({name:vr,version:br},{capabilities:{logging:{},resources:{},prompts:{},tools:{listChanged:true}}}),r={readOnly:e?.readOnly??false,activeTiers:e?.tiers??Wr,...e?.allowedTiers?{allowedTiers:e.allowedTiers}:{},hasIdocConfig:e?.hasIdocConfig??false,hasRfcConfig:e?.hasRfcConfig??false},s=new wt(r),i=n.registerTool.bind(n),o=e?.toolTimeoutMs??ht*1e3;n.registerTool=((h,...S)=>{let T=S.length-1,y=S[T];typeof y=="function"&&(S[T]=Sr(h,y,{toolTimeoutMs:o,logger:e?.logger,tlsVerifyDisabled:e?.httpOptions?.tlsVerify===false}));let b=i(h,...S);return s.registerHandle(h,b),b});let a={baseUrl:t.baseUrl,...e?.cacheDir?{persistDir:e.cacheDir}:{},...e?.logger?{onWarn:(h,S)=>e.logger?.warn({err:S},h)}:{}},c=e?.caches?.catalog??new G(a),l=e?.caches?.metadata??new k(a),d=Ye(t),u=new fe(d,e?.httpOptions);e?.userContext&&(u.userContext=e.userContext);let m=e?.exposePatterns,p=e?.sapClient??t.sapClient;Er(n),wr(n),ss(n,u,{baseUrl:t.baseUrl,sapClient:p,tlsVerify:e?.httpOptions?.tlsVerify}),ns(n,u,m),ts(n,u,{catalogV2Url:e?.catalogV2Url,catalogV4Url:e?.catalogV4Url,baseUrl:t.baseUrl,onDiscoveryMatch:e?.onDiscoveryMatch,...e?.concurrency?{concurrency:e.concurrency}:{}},c),ps(n,u,m,l),rs(n,u,l),Gr(n,u,l),us(n,u,m,l);let f=e?.idocStore??new ve,g=new gt(u,p,e?.partnerDefaults);return as(n,u,g,f),Br(n,f),e?.rfcAvailable&&e.rfcPool&&e.logger&&ys(n,e.rfcPool,e.logger),ds(n,s),Fr(n,u,l),Lr(n,u,l),jr(n),s.applyInitialVisibility(),{server:n,registry:s,httpClient:u}}var uc=z$1.object({snc_qop:z$1.enum(["1","2","3","8","9"]).optional(),snc_myname:z$1.string().min(1).optional(),snc_partnername:z$1.string().min(1).optional(),snc_lib:z$1.string().min(1).optional()}).superRefine((t,e)=>{let n=[t.snc_qop,t.snc_myname,t.snc_partnername,t.snc_lib].filter(r=>r!==void 0).length;if(n>0&&n<4){let r=[];t.snc_qop||r.push("SAP_SNC_QOP"),t.snc_myname||r.push("SAP_SNC_MYNAME"),t.snc_partnername||r.push("SAP_SNC_PARTNERNAME"),t.snc_lib||r.push("SAP_SNC_LIB"),e.addIssue({code:"custom",message:`SNC config incomplete: missing ${r.join(", ")} (D-12 all-or-none)`,path:["snc_qop"]});}});function Sn(t=process.env){let e=t.SAP_USERNAME,n=t.SAP_PASSWORD;if(!e||!n)return null;let r=!!(t.SAP_RFC_ASHOST&&t.SAP_RFC_SYSNR),s=!!(t.SAP_RFC_MSHOST&&t.SAP_RFC_GROUP);if(!r&&!s)return null;let i={user:e,passwd:n,client:t.SAP_CLIENT,lang:t.SAP_RFC_LANG??"EN"};r&&(i.ashost=t.SAP_RFC_ASHOST,i.sysnr=t.SAP_RFC_SYSNR),s&&(i.mshost=t.SAP_RFC_MSHOST,i.msserv=t.SAP_RFC_MSSERV,i.group=t.SAP_RFC_GROUP);let o=uc.safeParse({snc_qop:t.SAP_SNC_QOP,snc_myname:t.SAP_SNC_MYNAME,snc_partnername:t.SAP_SNC_PARTNERNAME,snc_lib:t.SAP_SNC_LIB});if(!o.success){let a=o.error.issues.map(c=>c.message).join("; ");throw new Error(`Invalid SNC configuration: ${a}`)}return o.data.snc_qop&&Object.assign(i,o.data),i}function Ss(t=process.env){try{return Sn(t)!==null}catch{return false}}var _t=class{constructor(e={}){this.options=e;this.discovery=e.discoveryClient??new ee;}discovery;jwks;jwksIssuer;get verifies(){return !!this.options.issuerUrl}async verify(e){let n=e.authorization??e.Authorization;if(typeof n!="string"||!n.startsWith("Bearer "))return {kind:"none"};let r=n.slice(7).trim();if(!r)return {kind:"none"};if(!this.options.issuerUrl)return this.withoutVerification(r);try{let s=await this.getKeySet(this.options.issuerUrl),{payload:i}=await jwtVerify(r,s,{issuer:void 0,...this.options.audience?{audience:this.options.audience}:{}});return typeof i.sub!="string"?{kind:"rejected",reason:"Token ohne sub-Claim"}:{kind:"user",context:{token:r,sub:i.sub,email:typeof i.email=="string"?i.email:void 0,name:typeof i.name=="string"?i.name:void 0,verified:!0}}}catch(s){return gc(r)?{kind:"rejected",reason:s instanceof Error?s.message:"Signaturpr\xFCfung fehlgeschlagen"}:{kind:"none"}}}withoutVerification(e){try{let n=decodeJwt(e);return typeof n.sub!="string"?{kind:"none"}:{kind:"user",context:{token:e,sub:n.sub,email:typeof n.email=="string"?n.email:void 0,name:typeof n.name=="string"?n.name:void 0,verified:!1}}}catch{return {kind:"none"}}}async getKeySet(e){if(this.jwks&&this.jwksIssuer===e)return this.jwks;let n=await this.discovery.discover(e);return this.jwks=createRemoteJWKSet(new URL(n.jwks_uri)),this.jwksIssuer=e,this.jwks}};function gc(t){let e=t.split(".");return e.length===3&&e.every(n=>n.length>0)}var hc="x-sap-username",yc="x-sap-password",Sc="x-sap-authorization";function Tn(t,e){let n=t[e]??t[e.toLowerCase()],r=Array.isArray(n)?n[0]:n;return typeof r=="string"&&r.length>0?r:void 0}function Tc(t){let e=Tn(t,hc),n=Tn(t,yc);if(e&&n)return {username:e,password:n};let r=Tn(t,Sc),s=r?/^Basic\s+(.+)$/i.exec(r.trim()):null;if(s?.[1]){let i=Buffer.from(s[1].trim(),"base64").toString("utf8"),o=i.indexOf(":");if(o>0&&o<i.length-1)return {username:i.slice(0,o),password:i.slice(o+1)}}}function An(t,e){let n=Tc(e);if(t.authType==="user-basic"){if(!n)return {kind:"missing",hint:`Destination "${t.name}" verlangt eine pers\xF6nliche SAP-Anmeldung: sende X-SAP-Username und X-SAP-Password (oder X-SAP-Authorization: Basic <base64 user:pass>) mit \u2014 im n8n-Node \u201EGuniWeb SAP" sind das die Felder SAP-Benutzer/SAP-Passwort des Credentials.`};let{name:r,baseUrl:s,sapClient:i}=t;return {kind:"ok",config:{name:r,baseUrl:s,authType:"basic",...i!==void 0?{sapClient:i}:{},username:n.username,password:n.password},username:n.username}}return n?{kind:"rejected",reason:`Destination "${t.name}" (authType ${t.authType}) nimmt keine pers\xF6nliche SAP-Anmeldung an \u2014 die Anfrage w\xFCrde unter dem konfigurierten technischen Zugang laufen. Lass X-SAP-Username/X-SAP-Password weg oder stelle die Destination auf authType user-basic um.`}:{kind:"ok",config:t}}var Ac=z$2.object({destination:z$2.string().min(1),label:z$2.string().max(200).optional(),readOnly:z$2.boolean().optional(),tiers:z$2.array(z$2.string()).optional()}).strict();function ae(t,e,n){t.setHeader("Cache-Control","no-store"),t.status(e).json(n);}function Ts(t){return t.ip??t.socket?.remoteAddress??"unknown"}function As(t,e){let n=t?.[e];return n==="true"||n==="1"||n===""}function bs(t){t.use("/admin",(e,n)=>{ae(n,404,{error:"Admin API not enabled",hint:"Start the server with --admin-token <secret> (or SAP_MCP_ADMIN_TOKEN) and --destinations <file>."});});}function vs(t,e){let{destinations:n,logger:r}=e,s=new Se({apiKey:e.adminToken,tokens:()=>[]});t.use("/admin",(c,l,d)=>{let u=c,m=l,p=Ts(u),f=s.authenticate(u.headers.authorization,p);if(f.kind==="rate-limited"){r?.warn({clientAddress:p,path:u.originalUrl??u.path,retryAfterSeconds:f.retryAfterSeconds},"Admin-API: zu viele fehlgeschlagene Anmeldeversuche von dieser Adresse"),m.setHeader("Retry-After",String(f.retryAfterSeconds)),ae(m,429,{error:"Too Many Requests",retryAfterSeconds:f.retryAfterSeconds});return}if(f.kind!=="api-key"){r?.warn({clientAddress:p,path:u.originalUrl??u.path},"Admin-API: Anfrage abgewiesen"),ae(m,401,{error:"Unauthorized"});return}d();});let i=()=>{if(n.source!=="file"||!n.filePath)throw new P("Admin API requires a destinations file: start the server with --destinations <file>.",409);return new Te({filePath:n.filePath,env:e.env})},o=(c,l)=>(d,u)=>{let m=d,p=u,f=Ts(m);(async()=>{try{let{status:g,body:h,changed:S}=l(m);if(S){let T=await n.reload();h.reloaded=T.ok,T.ok||(h.reloadErrors=T.errors),r?.info({action:c,clientAddress:f,target:m.params?.name??m.params?.ref??m.body?.destination,reloaded:T.ok},"Admin-API: \xC4nderung ausgef\xFChrt");}ae(p,g,h);}catch(g){if(g instanceof P){ae(p,g.status,{error:g.message});return}r?.warn({action:c,clientAddress:f,err:g instanceof Error?g.message:String(g)},"Admin-API: unerwarteter Fehler"),ae(p,500,{error:"Internal Server Error"});}})();},a=c=>{let l=c.body;if(l===null||typeof l!="object"||Array.isArray(l))throw new P("Request body must be a JSON object (Content-Type: application/json).",400);return l};t.get("/admin/destinations",o("destinations.list",()=>({status:200,body:{destinations:i().listDestinations()},changed:!1}))),t.put("/admin/destinations/:name",o("destinations.put",c=>{let l=c.params?.name??"",d=a(c),{replaced:u,warnings:m}=i().addDestination(l,d,{replace:!0,allowMissingEnv:As(c.query,"allowMissingEnv")});return {status:u?200:201,body:{name:l,replaced:u,warnings:m},changed:!0}})),t.delete("/admin/destinations/:name",o("destinations.delete",c=>{let l=c.params?.name??"",{removedTokens:d}=i().removeDestination(l,{force:As(c.query,"force")});return {status:200,body:{name:l,removedTokens:d},changed:!0}})),t.get("/admin/tokens",o("tokens.list",()=>({status:200,body:{tokens:i().listTokens()},changed:!1}))),t.post("/admin/tokens",o("tokens.issue",c=>{let l=Ac.safeParse(a(c));if(!l.success)throw new P(`Invalid body: ${l.error.issues.map(g=>`${g.path.join(".")||"(root)"}: ${g.message}`).join("; ")}`,400);let{destination:d,...u}=l.data,{token:m,hash:p,entry:f}=i().issueToken(d,u);return {status:201,body:{token:m,hash:p.replace(/^sha256:/,"").slice(0,12),destination:f.destination,label:f.label,policy:f.policy,createdAt:f.createdAt},changed:!0}})),t.delete("/admin/tokens/:ref",o("tokens.revoke",c=>{let l=c.params?.ref??"",d=i().revokeToken(l);return {status:200,body:{...d,revoked:!0},changed:!d.alreadyRevoked}})),t.use("/admin",(c,l)=>{ae(l,404,{error:"Not Found"});});}function ws(t,e){return e?.kind==="token"?t.destinations?.get(e.token.destination):t.destinations?t.destinations.defaultDestination():t.config}function Es(t,e){if(e?.kind!=="token"||!e.token.policy)return t;let n=e.token.policy;return {...t,readOnly:t.readOnly===true||n.readOnly===true,...n.tiers?{allowedTiers:[...n.tiers]}:{}}}function Cs(t,e){t.writeHead(503,{"Content-Type":"application/json"});let n=e?.names().length??0;t.end(JSON.stringify({error:"Keine Destination bestimmbar",hint:n===0?'Es ist noch keine Destination konfiguriert. Lege eine an \u2014 per Admin-API (PUT /admin/destinations/<name>) oder CLI (guniweb-sap-mcp destinations add) \u2014 und ein Token daf\xFCr, oder nenne sie "default".':'Es sind mehrere Destinations konfiguriert, aber keine hei\xDFt "default". Benenne eine Destination "default", lege Tokens an oder reduziere die Datei auf ein Ziel.'}));}var bn=class t{constructor(e){this.persistDir=e;}entries=new Map;static MAX_USER_SCOPES=100;for(e,n){let r=`${e.name}\0${e.baseUrl}\0${n??""}`,s=this.entries.get(r);if(!s){for(let o of this.entries.keys())o.startsWith(`${e.name}\0`)&&!o.startsWith(`${e.name}\0${e.baseUrl}\0`)&&this.entries.delete(o);n!==void 0&&this.evictOldestUserScopes(e);let i={baseUrl:e.baseUrl,...this.persistDir&&n===void 0?{persistDir:this.persistDir}:{}};s={catalog:new G(i),metadata:new k(i)},this.entries.set(r,s);}return s}evictOldestUserScopes(e){let n=`${e.name}\0${e.baseUrl}\0`,r=[...this.entries.keys()].filter(s=>s.startsWith(n)&&s!==n);for(;r.length>=t.MAX_USER_SCOPES;)this.entries.delete(r.shift());}};function Ps(t,e){if(e.kind==="missing"){t.writeHead(401,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"SAP login required",hint:e.hint}));return}t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:e.reason}));}function Rs(t,e){let n=t.logger;return !e||!n||typeof n.child!="function"?t:{...t,logger:n.child({sapUser:e})}}async function _s(t){let e=new _t({...t.jwtIssuer?{issuerUrl:t.jwtIssuer}:{},...t.jwtAudience?{audience:t.jwtAudience}:{}}),n=createMcpExpressApp({host:"0.0.0.0"}),r=new Se({apiKey:t.apiKey,tokens:()=>t.destinations?.tokens()??[]}),s=t.serverOptions?.logger;n.use((a,c,l)=>{let d=a,u=c,m=l;if(d.path==="/healthz"){m();return}if(d.path==="/admin"||d.path.startsWith("/admin/")){m();return}if(t.webhookSecret&&d.path==="/idoc"){m();return}let p=d.ip??d.socket?.remoteAddress??"unknown",f=r.authenticate(d.headers.authorization,p);if(f.kind==="rate-limited"){s?.warn({clientAddress:p,path:d.path,retryAfterSeconds:f.retryAfterSeconds},"MCP-Anfrage abgewiesen: zu viele fehlgeschlagene Anmeldeversuche von dieser Adresse"),u.setHeader("Retry-After",String(f.retryAfterSeconds)),u.status(429).json({error:"Too Many Requests",retryAfterSeconds:f.retryAfterSeconds});return}if(f.kind==="rejected"){s?.warn({clientAddress:p,path:d.path,reason:f.reason},"MCP-Anfrage abgewiesen"),u.status(401).json({error:"Unauthorized"});return}d.sapAuth=f,m();}),n.get("/healthz",(a,c)=>{c.status(200).json({status:"ok"});}),t.adminToken&&t.destinations?vs(n,{adminToken:t.adminToken,destinations:t.destinations,env:t.env??process.env,logger:s}):bs(n),t.transport==="http"?Cc(n,t,e):Pc(n,t,e);let i=pt(t.idocStore,{secret:t.webhookSecret,maxBodyBytes:t.webhookMaxBodyBytes});n.post("/idoc",(a,c)=>{i(a,c);}),n.use((a,c,l,d)=>{let u=a,m=l;if(u?.type==="entity.parse.failed"&&!m.headersSent){m.status(400).json({error:`Invalid JSON body: ${u.message??"parse error"}`});return}d(a);});let o=createServer(n);return new Promise(a=>{o.listen(t.port,()=>{let c=o.address(),l=typeof c=="object"&&c!==null?c.port:t.port;a({port:l,close:()=>new Promise((d,u)=>{o.close(m=>m?u(m):d());})});});})}function Cc(t,e,n){let r=new bn(e.serverOptions?.cacheDir),s=e.serverOptions?.logger;t.all("/mcp",(i,o)=>{let a=i,c=o,l=ws(e,a.sapAuth);if(!l){Cs(c,e.destinations);return}let d=An(l,a.headers);if(d.kind!=="ok"){s?.warn({destination:l.name,clientAddress:a.socket?.remoteAddress,reason:d.kind},d.kind==="missing"?"MCP-Anfrage ohne pers\xF6nliche SAP-Anmeldung an eine user-basic-Destination abgewiesen":"MCP-Anfrage mit pers\xF6nlicher SAP-Anmeldung an eine Destination mit technischem Benutzer abgewiesen"),Ps(c,d);return}let u=d.config,m=d.username,p=new StreamableHTTPServerTransport({sessionIdGenerator:void 0}),f=g=>{let{server:h}=Ke(u,{...Es(Rs(e.serverOptions,m),a.sapAuth),sapClient:u.sapClient,userContext:g,caches:r.for(u,m)});h.connect(p).then(()=>p.handleRequest(a,c,a.body)).catch(()=>{c.headersSent||(c.writeHead(500),c.end("Internal Server Error"));});};n.verify(a.headers).then(g=>{if(g.kind==="rejected"){c.writeHead(401,{"Content-Type":"application/json"}),c.end(JSON.stringify({error:"Unauthorized",reason:g.reason,hint:"Das mitgeschickte Bearer-Token hat die Signaturpr\xFCfung gegen SAP_JWT_ISSUER nicht bestanden."}));return}let h=g.kind==="user"?g.context:null;f(h);});});}function Pc(t,e,n){let r=new Map;t.get("/sse",(s,i)=>{let o=s,a=i,c=ws(e,o.sapAuth);if(!c){Cs(a,e.destinations);return}let l=An(c,o.headers??{});if(l.kind!=="ok"){Ps(a,l);return}let d=l.config,u=new SSEServerTransport("/message",a),{server:m,httpClient:p}=Ke(d,{...Es(Rs(e.serverOptions,l.username),o.sapAuth),sapClient:d.sapClient});m.connect(u).then(()=>u.start()).then(()=>{r.set(u.sessionId,{transport:u,server:m,httpClient:p}),u.onclose=()=>{r.delete(u.sessionId);};}).catch(()=>{a.headersSent||(a.writeHead(500),a.end("Internal Server Error"));});}),t.post("/message",(s,i)=>{let o=s,a=i,l=new URL(o.url??"",`http://${o.headers.host??"localhost"}`).searchParams.get("sessionId")??o.query?.sessionId;if(!l||!r.has(l)){a.writeHead(400,{"Content-Type":"application/json"}),a.end(JSON.stringify({error:"Invalid or missing session ID"}));return}let d=r.get(l);n.verify(o.headers).then(u=>{if(u.kind==="rejected"){a.writeHead(401,{"Content-Type":"application/json"}),a.end(JSON.stringify({error:"Unauthorized",reason:u.reason}));return}d.httpClient.userContext=u.kind==="user"?u.context:null,d.transport.handlePostMessage(o,a,o.body).catch(()=>{a.headersSent||(a.writeHead(500),a.end("Internal Server Error"));});});});}var Oc=`Usage: guniweb-sap-mcp [options]
|
|
327
327
|
guniweb-sap-mcp destinations list|add|remove ... (manage destinations.json)
|
|
328
328
|
guniweb-sap-mcp tokens issue|revoke|list ... (see: guniweb-sap-mcp tokens --help)
|
|
329
329
|
|
|
@@ -336,8 +336,10 @@ Options:
|
|
|
336
336
|
and points the server at it. No SAP_* needed; also
|
|
337
337
|
SAP_MCP_DEMO=true. Combine with --allow-write to
|
|
338
338
|
try create/update/delete. Not real SAP data.
|
|
339
|
-
--transport <stdio|http|sse> Transport mode (default: stdio)
|
|
340
|
-
|
|
339
|
+
--transport <stdio|http|sse> Transport mode (default: stdio).
|
|
340
|
+
Also SAP_MCP_TRANSPORT (for containers).
|
|
341
|
+
--port <number> HTTP/SSE port (default: 8808).
|
|
342
|
+
Also SAP_MCP_PORT (for containers).
|
|
341
343
|
--allow-write Enable write tools (sap_create, sap_update, sap_delete,
|
|
342
344
|
sap_function, sap_idoc_send). Since 1.0 the server is
|
|
343
345
|
READ-ONLY by default: first contact with a production
|
|
@@ -381,10 +383,12 @@ Configuration via environment variables: SAP_BASE_URL, SAP_AUTH_TYPE
|
|
|
381
383
|
SAP_USERNAME, SAP_PASSWORD, SAP_API_KEY, SAP_TLS_VERIFY,
|
|
382
384
|
SAP_HTTP_TIMEOUT_MS (single request timeout, default 30000),
|
|
383
385
|
SAP_MCP_TOOL_TIMEOUT (tool call budget in seconds, same as --tool-timeout),
|
|
386
|
+
SAP_MCP_TRANSPORT, SAP_MCP_PORT (same as --transport/--port; set in the
|
|
387
|
+
Docker image so a container needs no custom command),
|
|
384
388
|
SAP_WEBHOOK_SECRET, SAP_WEBHOOK_HOST, SAP_WEBHOOK_MAX_BODY_BYTES, etc.
|
|
385
389
|
See README for the full list.
|
|
386
390
|
|
|
387
391
|
Documentation, releases, issues: https://github.com/guniweb/guniweb-sap-mcp
|
|
388
392
|
Support: support@guniweb.de \u2014 https://guniweb.de/sap-mcp
|
|
389
|
-
`;async function Ic(){let t=process.argv.slice(2);if(t[0]!==void 0&&rr.has(t[0])){let y=await sr(t,{env:process.env,stdout:b=>process.stdout.write(b),stderr:b=>process.stderr.write(b)});process.exit(y);}let e=
|
|
393
|
+
`;async function Ic(){let t=process.argv.slice(2);if(t[0]!==void 0&&rr.has(t[0])){let y=await sr(t,{env:process.env,stdout:b=>process.stdout.write(b),stderr:b=>process.stderr.write(b)});process.exit(y);}let e=wn();e.help&&(process.stdout.write(Oc),process.exit(0));let n=e.transport??"stdio",r=!!e.adminToken;if(r&&!e.destinationsFile)throw new Error("--admin-token / SAP_MCP_ADMIN_TOKEN braucht --destinations <path> (bzw. SAP_MCP_DESTINATIONS): die Admin-API verwaltet diese Datei.");if(r&&n==="stdio")throw new Error("--admin-token / SAP_MCP_ADMIN_TOKEN wirkt nur im HTTP-Betrieb (--transport http|sse): im stdio-Betrieb gibt es keinen /admin-Endpunkt.");let s;if(e.demo){let y=["SAP_BASE_URL","SAP_AUTH_TYPE"].filter(b=>process.env[b]);if(e.destinationsFile||y.length>0)throw new Error(`--demo und eine echte SAP-Konfiguration schlie\xDFen sich aus (gefunden: ${[...e.destinationsFile?["--destinations/SAP_MCP_DESTINATIONS"]:[],...y].join(", ")}). F\xFCr die Demo alle SAP_*-Variablen und --destinations weglassen.`);s=await Gn();}let i=[],o=s?be.fromEnv(Dc(s.baseUrl)):e.destinationsFile?await be.fromFile(e.destinationsFile,{env:process.env,onWarn:y=>i.push(y),allowMissing:r}):be.fromEnv(process.env),a=o.defaultDestination(),c=o.tokens().length;if(!a&&(n==="stdio"||c===0)&&!r)throw new Error(`${o.filePath} definiert ${o.names().length} Destinations (${o.names().join(", ")}), aber keine hei\xDFt "default"${n==="stdio"?' \u2014 im stdio-Betrieb gibt es genau eine Verbindung, also muss eine Destination "default" hei\xDFen oder die Datei genau ein Ziel enthalten.':' und es sind keine Tokens konfiguriert, die eine Anfrage einem Ziel zuordnen k\xF6nnten. Benenne eine Destination "default" oder lege Tokens an.'}`);let l=Lt(process.env),d=ui({transport:{target:"pino/file",options:{destination:2}}});l.tlsVerify===false&&d.warn({tlsVerify:false},"SAP_TLS_VERIFY=false: TLS certificate validation disabled. ONLY use for development/test against self-signed certs. Production deployments MUST use SAP_TLS_VERIFY=true (or unset)."),l.clientCert&&d.info({hasCert:true,hasPassphrase:!!l.clientKeyPassphrase},"X.509 client certificate enabled for OData transport"),d.info(a?{baseUrl:a.baseUrl,authType:a.authType}:{destinations:o.names(),tokens:c},"SAP MCP server starting"),s&&d.warn({demo:true,mockSapUrl:s.baseUrl},`DEMO-MODUS: eingebautes Mock-SAP auf ${s.baseUrl} \u2014 Beispieldaten im Speicher (Gesch\xE4ftspartner, Kundenauftr\xE4ge, Produkte), keine echten SAP-Daten, keine Verbindung nach au\xDFen. \xC4nderungen gehen mit dem Prozess verloren.`);for(let y of i)d.warn(y);r&&!a&&c===0&&d.warn({file:o.filePath,destinations:o.names()},'Keine Standard-Destination und keine Tokens: /mcp antwortet 503, bis \xFCber die Admin-API (/admin) eine Destination "default" oder ein Token angelegt ist.'),o.source==="file"?(d.info({file:o.filePath,destinations:o.names(),defaultDestination:a?.name??null,tokens:c},"Destinations aus Datei geladen"),o.onChange(y=>{d.info({version:y.version,destinations:y.names,tokens:y.tokenCount},"Destinations neu geladen");}),o.onError(y=>{d.warn({errors:y},"Destinations-Datei ung\xFCltig \u2014 die letzte g\xFCltige Konfiguration bleibt in Kraft");})):d.info({destinations:o.names()},'Destination "default" aus SAP_*-Umgebung (keine destinations.json konfiguriert)'),e.allowWriteExplicit?d.warn("Schreibwerkzeuge sind aktiv (--allow-write): sap_create, sap_update, sap_delete, sap_function und sap_idoc_send k\xF6nnen Daten im SAP-System ver\xE4ndern."):d.info("Nur-Lese-Betrieb (Standard seit 1.0). Schreibwerkzeuge sind nicht registriert; mit --allow-write bzw. SAP_MCP_ALLOW_WRITE=true aktivieren.");for(let y of o.names())Nn(o.names().length>1?d.child({destination:y}):d,o.get(y)?.sapClient);e.transport&&e.transport!=="stdio"&&(process.env.SAP_JWT_ISSUER?d.info({issuer:process.env.SAP_JWT_ISSUER},"Eingehende Benutzer-Token werden gegen die JWKS des Issuers gepr\xFCft"):d.warn("SAP_JWT_ISSUER ist nicht gesetzt: eingehende Bearer-Token werden nur dekodiert, nicht auf ihre Signatur gepr\xFCft. F\xFCr Principal Propagation an SAP ist das vertretbar (SAP pr\xFCft selbst); eine lokale Entscheidung darf sich auf die Identit\xE4t aber nicht st\xFCtzen.")),e.expose&&d.info({exposePatterns:e.expose},"Entity set filtering active");let u=await fs(d),m;if(u){let y=Sn(process.env);if(y){y.snc_lib&&!existsSync(y.snc_lib)&&d.warn({path:y.snc_lib},"SAP_SNC_LIB path does not exist on this host: SNC will fail at first RFC connect. Verify SAP NW RFC SDK + Cryptographic Library installation."),m=new Rt(y,d);let b=!!y.snc_qop;d.info({ashost:y.ashost,mshost:y.mshost,snc:b,...b?{qop:y.snc_qop,myname:y.snc_myname}:{}},b?"RFC pool ready (SNC-secured)":"RFC pool ready");}else d.info("node-rfc loaded but no SAP_RFC_* config \u2014 RFC tier requires sap_enable_tools rfc + env vars to activate");}let p=new ve,f={exposePatterns:e.expose,idocStore:p,sapClient:a?.sapClient,partnerDefaults:{sndpor:e.sndpor,sndprt:e.sndprt,sndprn:e.sndprn,rcvpor:e.rcvpor,rcvprt:e.rcvprt,rcvprn:e.rcvprn},catalogV2Url:e.catalogV2Url,catalogV4Url:e.catalogV4Url,concurrency:e.concurrency,httpOptions:l,onDiscoveryMatch:y=>{d.info({path:y,attemptedPaths:3},"V2 catalog auto-discovery: matched path");}},g=e.toolTimeoutSeconds??ht;g===0?d.warn("Zeitbudget je Tool-Aufruf abgeschaltet (--tool-timeout 0): ein Aufruf kann unbegrenzt laufen und die Client-Verbindung bis zu deren eigenem Timeout blockieren."):d.info({toolTimeoutSeconds:g},"Zeitbudget je Tool-Aufruf aktiv"),e.cacheDir?d.info({cacheDir:e.cacheDir},"Cache-Ablage aktiv (\xFCberlebt Neustarts)"):d.info("Kein Cache-Verzeichnis konfiguriert (--cache-dir / SAP_CACHE_DIR): Katalog und Metadaten werden nach jedem Neustart erneut aus SAP geholt.");let h={...f,toolTimeoutMs:g*1e3,cacheDir:e.cacheDir,readOnly:e.readOnly,tiers:e.tiers,hasIdocConfig:En(e),hasRfcConfig:Ss(process.env),rfcAvailable:u,rfcPool:m,logger:d},S;if(n==="stdio"){if(!a)throw new Error("stdio-Betrieb ohne Standard-Destination.");if(a.authType==="user-basic")throw new Error("authType user-basic (pers\xF6nliche SAP-Anmeldung je Anfrage) gibt es nur im HTTP-Betrieb: stdio hat keinen Header, \xFCber den SAP-Benutzer und -Passwort mitkommen k\xF6nnten. Starte mit --transport http oder verwende authType basic.");let{server:y}=Ke(a,h),b=new StdioServerTransport;if(await y.connect(b),d.info("SAP MCP server connected on stdio transport"),e.webhookPort){e.webhookSecret||d.warn({port:e.webhookPort},"IDoc webhook has NO authentication: anyone reaching this port can inject IDocs. Set --webhook-secret (or SAP_WEBHOOK_SECRET) for production.");let D=await dr(e.webhookPort,p,{host:e.webhookHost,secret:e.webhookSecret,maxBodyBytes:e.webhookMaxBodyBytes});d.info({port:D.port,auth:e.webhookSecret?"secret":"open"},"IDoc webhook server listening");}}else {let y=e.port??8808,b=!!e.apiKey||c>0;!e.webhookSecret&&!b&&d.warn({port:y},"IDoc webhook (/idoc) and MCP endpoint have NO authentication. Set --api-key (or tokens in destinations.json) and --webhook-secret (or SAP_WEBHOOK_SECRET) for production."),o.watch(),S=await _s({transport:n,port:y,apiKey:e.apiKey,adminToken:e.adminToken,env:process.env,destinations:o,serverOptions:h,jwtIssuer:process.env.SAP_JWT_ISSUER,jwtAudience:process.env.SAP_JWT_AUDIENCE,idocStore:p,webhookSecret:e.webhookSecret,webhookMaxBodyBytes:e.webhookMaxBodyBytes}),d.info({transport:n,port:S.port,auth:[...e.apiKey?["api-key"]:[],...c>0?[`tokens(${c})`]:[]].join("+")||"open",adminApi:e.adminToken?"/admin (admin-token)":"off"},"SAP MCP server listening on HTTP"),e.adminToken&&d.info({file:o.filePath},"Admin-API aktiv unter /admin: Destinations und Tokens per HTTP verwaltbar (nur mit dem Admin-Secret; n8n-Tokens gelten dort nicht).");}for(let y of o.names()){let b=o.get(y);if(!b)continue;let D=o.names().length>1?d.child({destination:y}):d;if(b.authType==="user-basic"){D.info({baseUrl:b.baseUrl,sapClient:b.sapClient??null},"Pers\xF6nliche SAP-Anmeldung je Anfrage (authType user-basic): Selbsttest ohne Benutzer nicht m\xF6glich \u2014 jede Anfrage bringt X-SAP-Username/X-SAP-Password mit, SAP pr\xFCft Passwort und Berechtigungen.");continue}xn(D,b,l);}let T=async y=>{d.info({signal:y},"Shutting down...");try{o.close(),m&&await m.closeAll(),S&&await S.close(),s&&await s.close();}catch(b){d.error({err:b},"Shutdown error (continuing)");}process.exit(0);};process.on("SIGTERM",()=>T("SIGTERM")),process.on("SIGINT",()=>T("SIGINT"));}function Dc(t){let e=Kn(t),n=e.authType==="basic"?e:void 0;return {...process.env,SAP_BASE_URL:e.baseUrl,SAP_AUTH_TYPE:e.authType,SAP_USERNAME:n?.username,SAP_PASSWORD:n?.password,SAP_CLIENT:e.sapClient,SAP_TLS_VERIFY:void 0,SAP_CLIENT_CERT:void 0,SAP_CLIENT_KEY:void 0}}Ic().catch(t=>{process.stderr.write(`Fatal error: ${t instanceof Error?t.message:String(t)}
|
|
390
394
|
`),process.exit(1);});
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "guniweb-sap-mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "GuniWeb SAP S/4HANA & ECC MCP Server -- OData V2/V4, IDoc, and RFC/BAPI for n8n",
|
|
5
|
+
"mcpName": "io.github.guniweb/guniweb-sap-mcp",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"main": "dist/index.js",
|
|
7
8
|
"types": "dist/index.d.ts",
|