guniweb-sap-mcp 0.8.1 → 0.8.3
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/dist/index.js +45 -43
- package/package.json +2 -2
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
|
|
2
|
+
import {readFileSync,existsSync,watchFile,watch,unwatchFile,mkdirSync,writeFileSync,renameSync,unlinkSync}from'fs';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import go from'pino';import {minimatch}from'minimatch';import {z as z$1}from'zod/v4';import {lookup}from'dns/promises';import {connect}from'tls';import dn,{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,isAbsolute}from'path';import {z as z$2}from'zod';import {readFile}from'fs/promises';import {createRequire}from'module';import {pathToFileURL}from'url';import {XMLParser,XMLBuilder}from'fast-xml-parser';import {McpServer,ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {StreamableHTTPServerTransport}from'@modelcontextprotocol/sdk/server/streamableHttp.js';import vi from'express';var zn=16;function $e(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 Yn(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 Pi(t,e)}function Pi(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 A=Number.parseInt(process.env.SAP_WEBHOOK_MAX_BODY_BYTES,10);Number.isFinite(A)&&A>0&&(e.webhookMaxBodyBytes=A);}let i=t.indexOf("--max-request-bytes");if(i!==-1&&i<t.length-1){let A=Number.parseInt(t[i+1],10);Number.isFinite(A)&&A>0&&(e.maxRequestBytes=A);}if(e.maxRequestBytes===void 0&&process.env.SAP_MCP_MAX_REQUEST_BYTES){let A=Number.parseInt(process.env.SAP_MCP_MAX_REQUEST_BYTES,10);Number.isFinite(A)&&A>0&&(e.maxRequestBytes=A);}let o=t.indexOf("--transport");if(o!==-1&&o<t.length-1){let A=t[o+1];(A==="stdio"||A==="http"||A==="sse")&&(e.transport=A);}if(e.transport===void 0){let A=process.env.SAP_MCP_TRANSPORT?.trim();(A==="stdio"||A==="http"||A==="sse")&&(e.transport=A);}let a=t.indexOf("--port");if(a!==-1&&a<t.length-1&&(e.port=Number.parseInt(t[a+1],10)),e.port===void 0&&process.env.SAP_MCP_PORT){let A=Number.parseInt(process.env.SAP_MCP_PORT,10);Number.isFinite(A)&&A>0&&A<=65535&&(e.port=A);}let d=t.indexOf("--api-key");d!==-1&&d<t.length-1&&(e.apiKey=t[d+1]);let l=["sndpor","sndprt","sndprn","rcvpor","rcvprt","rcvprn"];for(let A of l){let oe=t.indexOf(`--${A}`);oe!==-1&&oe<t.length-1&&(e[A]=t[oe+1]);}let c=t.indexOf("--catalog-v2-url");c!==-1&&c<t.length-1&&(e.catalogV2Url=t[c+1]),!e.catalogV2Url&&process.env.ODATA_V2_CATALOG_PATH&&(e.catalogV2Url=process.env.ODATA_V2_CATALOG_PATH);let u=t.indexOf("--catalog-v4-url");u!==-1&&u<t.length-1&&(e.catalogV4Url=t[u+1]);let p=t.includes("--allow-write")||process.env.SAP_MCP_ALLOW_WRITE==="true";e.readOnly=!p,e.allowWriteExplicit=p;let m=t.indexOf("--tool-timeout"),f=m!==-1&&m<t.length-1?t[m+1]:process.env.SAP_MCP_TOOL_TIMEOUT;if(f!==void 0){let A=Number.parseInt(f,10);if(!Number.isFinite(A)||A<0)throw new Error(`--tool-timeout muss eine Zahl in Sekunden sein (0 schaltet das Budget ab), war: "${f}"`);e.toolTimeoutSeconds=A;}let g=t.indexOf("--cache-dir"),h=g!==-1&&g<t.length-1?t[g+1]:process.env.SAP_CACHE_DIR;h&&h.trim().length>0&&(e.cacheDir=h.trim());let b=t.indexOf("--governance");if(b!==-1&&b<t.length-1){let A=t[b+1];A&&A.trim().length>0&&(e.governanceModule=A.trim());}let y=t.indexOf("--concurrency"),E=y!==-1&&y<t.length-1?t[y+1]:process.env.SAP_CONCURRENCY;if(E!==void 0){let A=Number.parseInt(E,10);if(!Number.isFinite(A)||A<1)throw new Error(`--concurrency muss eine Zahl >= 1 sein, war: "${E}"`);e.concurrency=A;}let S=t.indexOf("--destinations"),T=S!==-1&&S<t.length-1?t[S+1]:process.env.SAP_MCP_DESTINATIONS;T&&T.trim().length>0&&(e.destinationsFile=T.trim());let x=t.indexOf("--admin-token"),G=x!==-1&&x<t.length-1?t[x+1]:process.env.SAP_MCP_ADMIN_TOKEN;if(G&&G.trim().length>0){let A=G.trim();if(A.length<zn)throw new Error(`--admin-token / SAP_MCP_ADMIN_TOKEN muss mindestens ${zn} Zeichen lang sein (z. B. openssl rand -base64 32).`);e.adminToken=A;}(t.includes("--demo")||process.env.SAP_MCP_DEMO==="true")&&(e.demo=true);let st=t.indexOf("--help"),it=t.indexOf("-h");(st!==-1||it!==-1)&&(e.help=true);let Ue=t.indexOf("--tiers");if(Ue!==-1&&Ue<t.length-1){let A=t[Ue+1].split(",").map(oe=>oe.trim()).filter(oe=>oe.length>0);e.tiers=A.length>0?A:void 0;}let zt=t.indexOf("--destination-name");zt!==-1&&zt<t.length-1&&(e.destinationName=t[zt+1]),!e.destinationName&&process.env.SAP_DESTINATION_NAME&&(e.destinationName=process.env.SAP_DESTINATION_NAME);let Yt=t.indexOf("--destination-service-url");Yt!==-1&&Yt<t.length-1&&(e.destinationServiceUrl=t[Yt+1]),!e.destinationServiceUrl&&process.env.SAP_DESTINATION_SERVICE_URL&&(e.destinationServiceUrl=process.env.SAP_DESTINATION_SERVICE_URL);let Xt=t.indexOf("--issuer");Xt!==-1&&Xt<t.length-1&&(e.issuer=t[Xt+1]),!e.issuer&&process.env.SAP_ISSUER&&(e.issuer=process.env.SAP_ISSUER);let Jt=t.indexOf("--name-id-format");Jt!==-1&&Jt<t.length-1&&(e.nameIdFormat=t[Jt+1]),!e.nameIdFormat&&process.env.SAP_NAME_ID_FORMAT&&(e.nameIdFormat=process.env.SAP_NAME_ID_FORMAT);let Zt=t.indexOf("--private-key"),Ai=Zt!==-1&&Zt<t.length-1?t[Zt+1]:void 0;e.privateKey=$e(Ai,"SAP_PRIVATE_KEY","SAP_PRIVATE_KEY_FILE");let Qt=t.indexOf("--certificate"),Ei=Qt!==-1&&Qt<t.length-1?t[Qt+1]:void 0;e.certificate=$e(Ei,"SAP_CERTIFICATE","SAP_CERTIFICATE_FILE");let en=t.indexOf("--clock-skew");en!==-1&&en<t.length-1&&(e.clockSkew=Number.parseInt(t[en+1],10)),e.clockSkew===void 0&&process.env.SAP_CLOCK_SKEW&&(e.clockSkew=Number.parseInt(process.env.SAP_CLOCK_SKEW,10));let tn=t.indexOf("--assertion-lifetime");return tn!==-1&&tn<t.length-1&&(e.assertionLifetime=Number.parseInt(t[tn+1],10)),e.assertionLifetime===void 0&&process.env.SAP_ASSERTION_LIFETIME&&(e.assertionLifetime=Number.parseInt(process.env.SAP_ASSERTION_LIFETIME,10)),t.includes("--version")&&(e.version=true),e}var ki=new Set(["--admin-token","--api-key","--assertion-lifetime","--cache-dir","--catalog-v2-url","--catalog-v4-url","--certificate","--clock-skew","--concurrency","--destination-name","--destination-service-url","--destinations","--expose","--governance","--issuer","--max-request-bytes","--name-id-format","--port","--private-key","--tiers","--tool-timeout","--transport","--webhook-host","--webhook-port","--webhook-secret"]),_i=new Set(["--allow-write","--demo","--help","-h","--read-only","--version"]);function Oi(t){let e=[],n=false;for(let r of t)n?n=false:ki.has(r)?n=true:_i.has(r)||e.push(r);return e}function Xn(t){let e=Oi(t);if(e.length>0)throw new Error(`Unbekannte Argumente: ${e.join(" ")}. Werte stehen mit Leerzeichen hinter dem Flag (--port 8808, nicht --port=8808). Alle Optionen: guniweb-sap-mcp --help`)}function nn(t){return !!(t.sndprn&&t.rcvprn)}function Y(t,e){return !e||e.length===0?true:e.some(n=>minimatch(t,n))}function Jn(t){if(t==="sse")throw new Error("--transport sse (bzw. SAP_MCP_TRANSPORT=sse) wurde in 0.8.0 entfernt: Die MCP-Spezifikation 2026-07-28 f\xFChrt HTTP+SSE als veraltet. Verwende --transport http (Streamable HTTP, Endpunkt /mcp); der n8n-Node und aktuelle MCP-Clients sprechen ihn.")}var Zn=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 rn(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=$e(void 0,"SAP_CLIENT_CERT","SAP_CLIENT_CERT_PATH",t),s=$e(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}:{}},d=Zn.safeParse(a);if(!d.success){let l=d.error.issues.map(c=>` - ${c.path.join(".")||"root"}: ${c.message}`).join(`
|
|
3
3
|
`);throw new Error(`Invalid HTTP options:
|
|
4
|
-
${l}`)}return d.data}function ot(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 Q=new AsyncLocalStorage;function Zn(){return randomBytes(6).toString("hex")}function ye(){return Q.getStore()}function Qn(t,e){return Q.run(t,e)}function at(){let t=Q.getStore();if(t)return t.sapCalls.count+=1,t.correlationId}function er(t){let e=Q.getStore();if(!e?.mutatingCalls)return;let n=(t??"get").toLowerCase();n!=="get"&&n!=="head"&&(e.mutatingCalls.count+=1);}function _i(t){if(!t)return;let e=t;try{e=new URL(t,"http://x").pathname;}catch{}let n=decodeURIComponent(e.split("/").filter(Boolean).at(-1)??"");return /^[A-Za-z_][\w.]*\(.*\)$/.test(n)?n:void 0}function Le(t,e){if(!t||typeof t!="object")return;let n=e.toLowerCase();for(let[r,s]of Object.entries(t))if(r.toLowerCase()===n){if(typeof s=="string")return s;if(Array.isArray(s)&&typeof s[0]=="string")return s[0]}}function ct(t){let e=Q.getStore();if(!e?.created)return;let n=_i(t);n&&(e.created.entity=n);}function tr(t=Date.now()){let e=Q.getStore();if(e?.deadline)return Math.max(0,e.deadline-t)}function dt(){return Q.getStore()?.signal?.aborted??false}var F=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 lt(){let t=Q.getStore();if(!t?.signal?.aborted)return;let e=t.signal.reason?.name==="TimeoutError";throw new F(e,t.toolName)}function nr(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 Se(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(d=>({code:d.code??"",message:d.message??"",severity:d.severity??"error",target:d.target}));return {code:n.code??"UNKNOWN",message:s,target:n.target,details:a,rawResponse:t}}function sn(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 on(t){let e=ce(t).toLowerCase();return /anmeldung fehlgeschlagen|anmeldefehler|logon (error|failed)|login failed|unauthori[sz]ed/.test(e)}var an="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 Ii(t){return /no authorization for operation\s+\d+\s+on object/i.test(t)}var Ni="SAP-KBA 3421507 nennt daf\xFCr zwei Ursachen, und keine ist die Berechtigung: (1) der Objektschl\xFCssel ist falsch formatiert \u2014 er geht zehnstellig mit f\xFChrenden Nullen hinaus, z. B. 0000012345 statt 12345; (2) der BusinessObjectTypeName passt nicht zum Objekt (Kundenauftrag: BUS2032). Dritte M\xF6glichkeit: Kopfzeile gegen Abfrageparameter verwechselt \u2014 beim schreibenden POST sind es Kopfzeilen, beim lesenden Aufruf Abfrageparameter. Erst danach lohnt der Blick in PFCG/SU53.";function ce(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 Di(t){return /\/IW[A-Z]+\/[A-Z_]+\/\d+/i.exec(t)?.[0]}var xi=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 Mi(t){let e=t?.code;if(typeof e=="string")return e;let n=t?.cause;if(typeof n?.code=="string")return n.code}function Ui(t){let n=(t?.cause?.cert??t?.cert)?.issuer?.CN;return typeof n=="string"?n:void 0}function be(t,e){if(t instanceof F)return {code:t.timedOut?"TOOL_TIMEOUT":"ABORTED",message:t.message};let n=Mi(t);if(n&&xi.has(n)){let i=Ui(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 $i(t.response.status,t.response.data,e);let r=t;if(typeof r?.message=="string"&&r.message.length>0)return sn(r.message)?on(r.message)?{code:"AUTH_FAILED",message:`Anmeldung abgelehnt (SAP-Anmeldeseite: ${ce(r.message)}).`,guidance:an}:{code:"HTML_RESPONSE",message:`SAP hat eine HTML-Seite statt Daten geliefert: ${ce(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 sn(t.message)?on(t.message)?{code:"AUTH_FAILED",message:`Anmeldung abgelehnt (SAP-Anmeldeseite: ${ce(t.message)}).`,guidance:an}:{code:"HTML_RESPONSE",message:`SAP hat eine HTML-Seite statt Daten geliefert: ${ce(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 $i(t,e,n){let r=typeof e=="string"?e:JSON.stringify(e??""),s=Di(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(Ii(r))return {code:"MEDIA_OBJECT_MISMATCH",httpStatus:t,...s?{sapCode:s}:{},message:rr(e)??"SAP meldet fehlende Berechtigung f\xFCr das Objekt \u2014 meist ist es aber der Objektschl\xFCssel oder der Objekttyp.",guidance:Ni};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:rr(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(sn(e))return on(e)?{code:"AUTH_FAILED",httpStatus:t,message:`Anmeldung abgelehnt (SAP-Anmeldeseite, HTTP ${t}: ${ce(e)}).`,guidance:an}:{code:"HTML_RESPONSE",httpStatus:t,message:`SAP hat eine HTML-Fehlerseite geliefert (HTTP ${t}): ${ce(e)}`,guidance:"Der Pfad wird von SAP nicht als OData-Service beantwortet. Bei Katalogpfaden sap_discover_services statt sap_query verwenden."};let i=Se(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 rr(t){let e=Se(t);if(e.message&&!(e.message==="Unknown error"||e.message==="Unknown SAP error"))return e.message}var Fi=5e3;function sr(t){let e=t?.issuer?.CN??t?.issuer?.O;return Array.isArray(e)?e.join(", ")||void 0:typeof e=="string"?e:void 0}function O(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 Hi(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 ji(t,e){let n=Date.now();try{let{address:r,family:s}=await e(t);return O("dns",n,"ok",`${t} l\xF6st auf ${r} auf.`,{info:{address:r,family:s}})}catch(r){let s=be(r);return O("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 Vi(t,e,n,r,s){let i=Date.now(),o=a=>new Promise((d,l)=>{let c=s({host:t,port:e,rejectUnauthorized:a,servername:t},()=>{let u=c.getPeerCertificate(true);c.destroy(),d(u);});c.setTimeout(r,()=>{c.destroy(),l(Object.assign(new Error(`TLS-Handshake nach ${r} ms ohne Antwort`),{code:"ETIMEDOUT"}));}),c.on("error",u=>{c.destroy(),l(u);});});try{let a=await o(n),d=sr(a);return O("tls",i,"ok",n?`TLS-Verbindung steht, Zertifikatskette ist verifizierbar.${d?` Aussteller: ${d}.`:""}`:`TLS-Verbindung steht. Die Zertifikatspr\xFCfung ist \xFCber SAP_TLS_VERIFY=false abgeschaltet \u2014 die Kette wurde NICHT verifiziert.${d?` Aussteller: ${d}.`:""}`,{info:{verified:n,...d?{issuer:d}:{},...a?.subject?.CN?{subject:String(a.subject.CN)}:{},...a?.valid_to?{validTo:a.valid_to}:{}}})}catch(a){let d=be(a);if(d.code==="TLS_CHAIN_INCOMPLETE"){let l;try{let c=await o(!1);l=sr(c);}catch{}return O("tls",i,"failed",l?`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar. Aussteller des Serverzertifikats: ${l}.`:"Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar.",{guidance:d.guidance,...l?{info:{issuer:l}}:{}})}return O("tls",i,"failed",d.message,{guidance:d.guidance})}}async function Wi(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 [O("auth",n,"ok","Anmeldung am Gateway erfolgreich."),O("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}}),O("catalog",n,"ok",`Servicekatalog erreichbar (HTTP ${r.status}).`)]}catch(r){let s=be(r),i=s.httpStatus===401||s.code==="AUTH_FAILED";return i&&!e?[O("auth",n,"ok","Zugangsdaten wurden gesendet."),O("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."}),O("catalog",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor.")]:i?[O("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.`}),O("client",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor."),O("catalog",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor.")]:[O("auth",n,"ok","Anmeldung am Gateway erfolgreich."),O("client",n,"ok",e?`Mandant ${e} angenommen.`:"Kein Mandant konfiguriert."),O("catalog",n,"failed",s.message,{guidance:s.guidance??"Katalogdienst in /IWFND/MAINT_SERVICE pr\xFCfen. Der Server probiert drei bekannte Katalogpfade durch."})]}}async function pt(t,e){let n=Date.now(),r=e.budgetMs??Fi,s=e.deps?.lookup??lookup,i=e.deps?.connectTls??connect,o=[],a=Hi(e.baseUrl);if(!a)return o.push(O("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(d=>O(d,n,"skipped","Nicht gepr\xFCft \u2014 die Basis-URL ist unbrauchbar."))),ut(o,n);if(o.push(await ji(a.host,s)),o[0].status==="failed")return o.push(...["tls","auth","client","catalog"].map(d=>O(d,n,"skipped","Nicht gepr\xFCft \u2014 der Name war nicht aufl\xF6sbar."))),ut(o,n);if(a.isTls){let d=Math.max(500,r-(Date.now()-n));if(o.push(await Vi(a.host,a.port,e.tlsVerify!==false,d,i)),o[o.length-1].status==="failed")return o.push(...["auth","client","catalog"].map(l=>O(l,n,"skipped","Nicht gepr\xFCft \u2014 die TLS-Stufe scheiterte zuvor."))),ut(o,n)}else o.push(O("tls",n,"skipped","Nicht gepr\xFCft \u2014 SAP_BASE_URL verwendet http, nicht https."));return o.push(...await Wi(t,e.sapClient)),ut(o,n)}function ut(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 we=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 dn.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 cn(t){return createHash("sha256").update(t).digest("hex")}var Be=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});}},mt=class{clientTokenManager;cache;config;constructor(e){this.config=e,this.clientTokenManager=new we(e.tokenServiceUrl,e.clientId,e.clientSecret),this.cache=new Be;}async getToken(e){if(!e)return this.clientTokenManager.getToken();let n=cn(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 dn.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 d=parseInt(a.expires_in,10),l=Number.isFinite(d)&&d>0?d:3600,c=Date.now()+(l-60)*1e3;return this.cache.set(n,a.value,c),a.value}};var Fe=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 dn.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 de=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 dn.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 no={email:"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",sapUsername:"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"};function ir(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 ro(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 so(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 ft=class{cache;config;constructor(e){ir(e.privateKey,"private key"),ir(e.certificate,"certificate"),this.config={...e,clockSkew:e.clockSkew??120,assertionLifetime:e.assertionLifetime??300},this.cache=new Be;}async getToken(e){if(!e)throw new Error("saml-bearer auth requires a user token for principal propagation");let n=cn(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=no[this.config.nameIdFormat],a=new Date(s-this.config.clockSkew*1e3).toISOString(),d=new Date(s+this.config.assertionLifetime*1e3).toISOString(),l=new Date(s).toISOString(),c=ro({assertionId:`_${randomUUID()}`,issuer:this.config.issuer,nameId:i,nameIdFormat:o,recipient:this.config.tokenServiceUrl,audience:this.config.clientId,notBefore:a,notOnOrAfter:d,authnInstant:l}),u=so(c,this.config.privateKey,this.config.certificate),p=Buffer.from(u).toString("base64"),m=await dn.post(this.config.tokenServiceUrl,new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:saml2-bearer",assertion:p,client_id:this.config.clientId}).toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}}),{access_token:f,expires_in:g}=m.data,h=typeof g=="number"&&g>0?g:3600,b=Date.now()+(h-60)*1e3;return this.cache.set(n,f,b),f}};var io="[geschwaerzt]",oo=new Set(["accept","accept-encoding","content-type","content-length","if-match","if-none-match","x-http-method","x-requested-with"]);function or(t){let e="'(?:[^']|'')*'",n=new RegExp(`\\b(guid|datetimeoffset|datetime|time|binary|X)${e}|${e}|\\b\\d+(?:\\.\\d+)?[dfmDFM]?\\b`,"gi");return t.replace(n,(r,s)=>s?`${s}'<${s}>'`:r.startsWith("'")?"'<str>'":"<num>")}function ao(t){return t.replace(/\(([^)]*)\)/g,(e,n)=>`(${or(n)})`)}function co(t){if(t===null||typeof t!="object")return t;let e={};for(let[n,r]of Object.entries(t))e[n]=oo.has(n.toLowerCase())?r:io;return e}var gt={paths:["headers","url",'params["$filter"]','params["$search"]'],censor:(t,e)=>{let n=e[e.length-1];return n==="headers"?co(t):typeof t!="string"?t:n==="url"?ao(t):or(t)}};function ar(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 po=3e4,ee=uo({name:"sap-http-client",redact:gt,transport:{target:"pino/file",options:{destination:2}}});function mo(t,e){return typeof t=="string"?t.trimStart().startsWith("<!DOCTYPE"):Buffer.isBuffer(t)?String(e?.["content-type"]??"").includes("text/html")?true:t.subarray(0,32).toString("latin1").trimStart().startsWith("<!DOCTYPE"):false}function fo(t){return t.startsWith("http://")||t.startsWith("https://")}function go(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 Te=class{constructor(e,n={}){this.destination=e;switch(this.httpsAgent=ar(n),this.requestTimeoutMs=n.requestTimeoutMs??po,e.auth.type){case "oauth2":this.tokenManager=new we(e.auth.tokenServiceUrl,e.auth.clientId,e.auth.clientSecret);break;case "ias":this.tokenManager=new Fe({issuerUrl:e.auth.tenantUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new de);break;case "xsuaa":this.tokenManager=new Fe({issuerUrl:e.auth.xsuaaUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new de);break;case "btp-principal":this.tokenManager=new mt({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 ft({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){lt();let n=e.method!=="get",r=ye(),s=at();er(e.method);let 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),d=this.destination.headers??{},l={};if(n){let p=await this.fetchCsrfToken({...d,...a},e.url);p.token&&(l={"x-csrf-token":p.token},p.cookies&&(l.Cookie=p.cookies));}let c={...d,...a,...l,...e.headers},u={method:e.method,url:this.resolveUrl(e.url),data:e.data,headers:c,params:Object.keys(i).length>0?i:void 0,...e.responseType?{responseType:e.responseType}:{},httpsAgent:this.httpsAgent,timeout:this.effectiveTimeoutMs(),signal:r?.signal,beforeRedirect:p=>{this.httpsAgent&&(p.agent=this.httpsAgent);let m=p.headers??{};for(let[f,g]of Object.entries(c))m[f]||(m[f]=g);}};ee.info({...s?{correlationId:s,tool:r?.toolName}:{},...r?.traceId?{traceId:r.traceId}:{},method:u.method,url:u.url,headers:u.headers,params:u.params,hasAuth:!!u.headers.Authorization||!!u.headers.APIKey},"SAP HTTP request outgoing");try{let p=await dn(u);if(ee.info({...s?{correlationId:s}:{},status:p.status,contentType:p.headers["content-type"]},"SAP HTTP response OK"),mo(p.data,p.headers))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 m=e.expectedStatuses?.includes(p.response.status)??false;(m?ee.info.bind(ee):ee.error.bind(ee))({...s?{correlationId:s}:{},status:p.response.status,statusText:p.response.statusText,contentType:p.response.headers?.["content-type"],url:u.url,...m?{expected:true}:{},dataType:typeof p.response.data,dataLength:typeof p.response.data=="string"?p.response.data.length:void 0},m?"SAP HTTP response (expected)":"SAP HTTP response ERROR");}else p instanceof Error&&ee.error({message:p.message,name:p.name},"SAP HTTP connection error");if(n&&this.isCsrf403(p)){lt();try{let m=await this.fetchCsrfToken({...d,...a},e.url),f={...u.headers};m.token&&(f["x-csrf-token"]=m.token,m.cookies&&(f.Cookie=m.cookies)),at();let g=await dn({...u,headers:f});return {data:g.data,headers:g.headers,status:g.status}}catch(m){throw this.toSapError(m)}}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(fo(e))return e;let{origin:n,path:r}=go(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){lt(),at();try{let o={...e,"x-csrf-token":"fetch"},a={};this.destination.sapClient&&(a["sap-client"]=this.destination.sapClient);let d=await dn.get(i,{headers:o,params:Object.keys(a).length>0?a:void 0,validateStatus:()=>!0,timeout:this.effectiveTimeoutMs(),signal:ye()?.signal,httpsAgent:this.httpsAgent,beforeRedirect:c=>{this.httpsAgent&&(c.agent=this.httpsAgent);let u=c.headers??{};for(let[p,m]of Object.entries(o))u[p]||(u[p]=m);}}),l=d.headers["x-csrf-token"];if(ee.debug({url:i,status:d.status,hasToken:!!l},"CSRF token fetch result"),l&&l.toLowerCase()!=="required"){let u=(d.headers["set-cookie"]??[]).map(p=>p.split(";")[0]).join("; ");return {token:l,cookies:u}}}catch{}}return {token:"",cookies:""}}isCsrf403(e){return isAxiosError(e)&&e.response?.status===403}effectiveTimeoutMs(){let e=tr();return e===void 0?this.requestTimeoutMs:Math.max(1,Math.min(this.requestTimeoutMs,e))}toSapError(e){if(e instanceof F)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=ye()?.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?Se(e.response.data):e instanceof Error?{code:"CONNECTION_ERROR",message:e.message,details:[],rawResponse:void 0}:Se(e)}};function cr(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 dr(t,e,n,r){try{let s=new Te(ot(e),n),i=await pt(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 le(t){return `/Date(${Date.parse(t.length===10?`${t}T00:00:00Z`:t)})/`}var T=(t,e,n,r=true)=>({name:t,type:"Edm.String",maxLength:e,...n?{label:n}:{},nullable:r}),je=(t,e,n=15,r=3)=>({name:t,type:"Edm.Decimal",precision:n,scale:r,...e?{label:e}:{}}),ue=(t,e)=>({name:t,type:"Edm.DateTime",precision:7,...e?{label:e}:{}}),lr=(t,e)=>({name:t,type:"Edm.Boolean",...e?{label:e}:{}}),pn="/sap/opu/odata/sap",ho={technicalName:"API_BUSINESS_PARTNER",title:"Business Partner (A2X)",description:"Business partners, customers and suppliers with their addresses.",path:`${pn}/API_BUSINESS_PARTNER`,namespace:"API_BUSINESS_PARTNER",entityTypes:[{set:"A_BusinessPartner",keys:["BusinessPartner"],autoKey:{prefix:"10",width:7},properties:[T("BusinessPartner",10,"Business Partner",false),T("Customer",10,"Customer"),T("Supplier",10,"Supplier"),T("BusinessPartnerCategory",1,"BP Category"),T("BusinessPartnerFullName",81,"Full Name"),T("BusinessPartnerName",81,"Name"),T("OrganizationBPName1",40,"Organization Name 1"),T("FirstName",40,"First Name"),T("LastName",40,"Last Name"),T("BusinessPartnerGrouping",4,"Grouping"),T("SearchTerm1",20,"Search Term"),T("Industry",4,"Industry"),T("CreatedByUser",12,"Created By"),ue("CreationDate","Created On"),lr("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:[T("BusinessPartner",10,"Business Partner",false),T("AddressID",10,"Address ID",false),ue("ValidityStartDate","Valid From"),ue("ValidityEndDate","Valid To"),T("StreetName",60,"Street"),T("HouseNumber",10,"House Number"),T("PostalCode",10,"Postal Code"),T("CityName",40,"City"),T("Country",3,"Country"),T("Region",3,"Region"),T("Language",2,"Language")],navigations:[{name:"to_BusinessPartner",target:"A_BusinessPartner",multiplicity:"1",join:[["BusinessPartner","BusinessPartner"]]}]}],functionImports:[]},yo={technicalName:"API_SALES_ORDER_SRV",title:"Sales Order (A2X)",description:"Sales orders with items \u2014 read, create (deep insert), change, release.",path:`${pn}/API_SALES_ORDER_SRV`,namespace:"API_SALES_ORDER_SRV",entityTypes:[{set:"A_SalesOrder",keys:["SalesOrder"],autoKey:{prefix:"45",width:7},properties:[T("SalesOrder",10,"Sales Order",false),T("SalesOrderType",4,"Order Type"),T("SalesOrganization",4,"Sales Organization"),T("DistributionChannel",2,"Distribution Channel"),T("OrganizationDivision",2,"Division"),T("SoldToParty",10,"Sold-to Party"),T("PurchaseOrderByCustomer",35,"Customer Reference"),T("CreatedByUser",12,"Created By"),ue("CreationDate","Created On"),ue("LastChangeDateTime","Last Changed"),ue("RequestedDeliveryDate","Requested Delivery Date"),je("TotalNetAmount","Net Value",16,3),T("TransactionCurrency",5,"Currency"),T("OverallSDProcessStatus",1,"Overall Status"),T("OverallDeliveryStatus",1,"Delivery Status")],navigations:[{name:"to_Item",target:"A_SalesOrderItem",multiplicity:"*",join:[["SalesOrder","SalesOrder"]]}]},{set:"A_SalesOrderItem",keys:["SalesOrder","SalesOrderItem"],properties:[T("SalesOrder",10,"Sales Order",false),T("SalesOrderItem",6,"Item",false),T("Material",40,"Material"),T("SalesOrderItemText",40,"Item Description"),T("SalesOrderItemCategory",4,"Item Category"),je("RequestedQuantity","Order Quantity",15,3),T("RequestedQuantityUnit",3,"Unit"),je("NetAmount","Net Value",16,3),T("TransactionCurrency",5,"Currency"),T("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"}]},So={technicalName:"API_PRODUCT_SRV",title:"Product Master (A2X)",description:"Products (materials) with descriptions.",path:`${pn}/API_PRODUCT_SRV`,namespace:"API_PRODUCT_SRV",entityTypes:[{set:"A_Product",keys:["Product"],autoKey:{prefix:"DEMO-",width:8},properties:[T("Product",40,"Product",false),T("ProductType",4,"Product Type"),T("ProductGroup",9,"Product Group"),T("BaseUnit",3,"Base Unit"),je("GrossWeight","Gross Weight",13,3),je("NetWeight","Net Weight",13,3),T("WeightUnit",3,"Weight Unit"),T("CreatedByUser",12,"Created By"),ue("CreationDate","Created On"),lr("IsMarkedForDeletion","Deletion Flag")],navigations:[{name:"to_Description",target:"A_ProductDescription",multiplicity:"*",join:[["Product","Product"]]}]},{set:"A_ProductDescription",keys:["Product","Language"],properties:[T("Product",40,"Product",false),T("Language",2,"Language",false),T("ProductDescription",40,"Description")],navigations:[]}],functionImports:[]},ht=[ho,yo,So],ln="MCP_DEMO",bo=[["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"]],wo=[["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]],To=[["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 un(t,e=3){return t.toFixed(e)}function mn(){let t=[],e=[];for(let[d,l,c,u,p,m,f,g,h,b,y,E]of bo){let S=l==="1",v=S?`${c} ${u}`:c;t.push({BusinessPartner:d,Customer:b==="C"||b==="B"?d:"",Supplier:b==="S"||b==="B"?d:"",BusinessPartnerCategory:l,BusinessPartnerFullName:v,BusinessPartnerName:v,OrganizationBPName1:S?"":c,FirstName:S?c:"",LastName:S?u:"",BusinessPartnerGrouping:S?"BP02":"BP01",SearchTerm1:(S?u:c.split(" ")[0]).toUpperCase().slice(0,20),Industry:y,CreatedByUser:ln,CreationDate:le(E),IsMarkedForArchiving:false}),e.push({BusinessPartner:d,AddressID:String(2e4+t.length),ValidityStartDate:le(E),ValidityEndDate:le("9999-12-31"),StreetName:p,HouseNumber:m,PostalCode:f,CityName:g,Country:"DE",Region:h,Language:"DE"});}let n=[],r=[],s=new Map,i=new Map;wo.forEach(([d,l,c,u,p,m,f,g,h],b)=>{s.set(d,h),i.set(d,g),n.push({Product:d,ProductType:l,ProductGroup:c,BaseUnit:u,GrossWeight:p,NetWeight:m,WeightUnit:"KG",CreatedByUser:ln,CreationDate:le(`2023-11-${String(6+b).padStart(2,"0")}`),IsMarkedForDeletion:false}),r.push({Product:d,Language:"DE",ProductDescription:f}),r.push({Product:d,Language:"EN",ProductDescription:g});});let o=[],a=[];for(let[d,l,c,u,p,m,f,g]of To){let h=0;g.forEach(([b,y],E)=>{let S=y*(s.get(b)??0);h+=S,a.push({SalesOrder:d,SalesOrderItem:String((E+1)*10),Material:b,SalesOrderItemText:i.get(b)??b,SalesOrderItemCategory:"TAN",RequestedQuantity:un(y),RequestedQuantityUnit:"ST",NetAmount:un(S),TransactionCurrency:"EUR",ProductionPlant:"1010"});}),o.push({SalesOrder:d,SalesOrderType:"OR",SalesOrganization:"1010",DistributionChannel:"10",OrganizationDivision:"00",SoldToParty:l,PurchaseOrderByCustomer:c,CreatedByUser:ln,CreationDate:le(u),LastChangeDateTime:le(`${u}T09:30:00Z`),RequestedDeliveryDate:le(p),TotalNetAmount:un(h),TransactionCurrency:"EUR",OverallSDProcessStatus:m,OverallDeliveryStatus:f});}return {A_BusinessPartner:t,A_BusinessPartnerAddress:e,A_SalesOrder:o,A_SalesOrderItem:a,A_Product:n,A_ProductDescription:r}}function vo(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function Ao(t,e){return `assoc_${t.set}_${e}`}function ur(t){let e=t.namespace,n=[],r=[],s=[];for(let a of t.entityTypes){let d=a.properties.map(c=>{let u=[`Name="${c.name}"`,`Type="${c.type}"`,`Nullable="${c.nullable===false||a.keys.includes(c.name)?"false":"true"}"`];return c.maxLength!==void 0&&u.push(`MaxLength="${c.maxLength}"`),c.precision!==void 0&&u.push(`Precision="${c.precision}"`),c.scale!==void 0&&u.push(`Scale="${c.scale}"`),c.label&&u.push(`sap:label="${vo(c.label)}"`),a.keys.includes(c.name)&&u.push('sap:creatable="true" sap:updatable="false"'),` <Property ${u.join(" ")}/>`}).join(`
|
|
5
|
-
`),l=a.navigations.map(c=>{let u=
|
|
4
|
+
${l}`)}return d.data}function ot(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 Q=new AsyncLocalStorage;function Qn(){return randomBytes(6).toString("hex")}function ye(){return Q.getStore()}function er(t,e){return Q.run(t,e)}function at(){let t=Q.getStore();if(t)return t.sapCalls.count+=1,t.correlationId}function tr(t){let e=Q.getStore();if(!e?.mutatingCalls)return;let n=(t??"get").toLowerCase();n!=="get"&&n!=="head"&&(e.mutatingCalls.count+=1);}function Di(t){if(!t)return;let e=t;try{e=new URL(t,"http://x").pathname;}catch{}let n=decodeURIComponent(e.split("/").filter(Boolean).at(-1)??"");return /^[A-Za-z_][\w.]*\(.*\)$/.test(n)?n:void 0}function Le(t,e){if(!t||typeof t!="object")return;let n=e.toLowerCase();for(let[r,s]of Object.entries(t))if(r.toLowerCase()===n){if(typeof s=="string")return s;if(Array.isArray(s)&&typeof s[0]=="string")return s[0]}}function ct(t){let e=Q.getStore();if(!e?.created)return;let n=Di(t);n&&(e.created.entity=n);}function nr(t=Date.now()){let e=Q.getStore();if(e?.deadline)return Math.max(0,e.deadline-t)}function dt(){return Q.getStore()?.signal?.aborted??false}var F=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 lt(){let t=Q.getStore();if(!t?.signal?.aborted)return;let e=t.signal.reason?.name==="TimeoutError";throw new F(e,t.toolName)}function rr(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 Se(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(d=>({code:d.code??"",message:d.message??"",severity:d.severity??"error",target:d.target}));return {code:n.code??"UNKNOWN",message:s,target:n.target,details:a,rawResponse:t}}function sn(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 on(t){let e=ce(t).toLowerCase();return /anmeldung fehlgeschlagen|anmeldefehler|logon (error|failed)|login failed|unauthori[sz]ed/.test(e)}var an="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 Mi(t){return /no authorization for operation\s+\d+\s+on object/i.test(t)}var Ui="SAP-KBA 3421507 nennt daf\xFCr zwei Ursachen, und keine ist die Berechtigung: (1) der Objektschl\xFCssel ist falsch formatiert \u2014 er geht zehnstellig mit f\xFChrenden Nullen hinaus, z. B. 0000012345 statt 12345; (2) der BusinessObjectTypeName passt nicht zum Objekt (Kundenauftrag: BUS2032). Dritte M\xF6glichkeit: Kopfzeile gegen Abfrageparameter verwechselt \u2014 beim schreibenden POST sind es Kopfzeilen, beim lesenden Aufruf Abfrageparameter. Erst danach lohnt der Blick in PFCG/SU53.";function ce(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 $i(t){return /\/IW[A-Z]+\/[A-Z_]+\/\d+/i.exec(t)?.[0]}var Li=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 Bi(t){let e=t?.code;if(typeof e=="string")return e;let n=t?.cause;if(typeof n?.code=="string")return n.code}function Fi(t){let n=(t?.cause?.cert??t?.cert)?.issuer?.CN;return typeof n=="string"?n:void 0}function be(t,e){if(t instanceof F)return {code:t.timedOut?"TOOL_TIMEOUT":"ABORTED",message:t.message};let n=Bi(t);if(n&&Li.has(n)){let i=Fi(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 Hi(t.response.status,t.response.data,e);let r=t;if(typeof r?.message=="string"&&r.message.length>0)return sn(r.message)?on(r.message)?{code:"AUTH_FAILED",message:`Anmeldung abgelehnt (SAP-Anmeldeseite: ${ce(r.message)}).`,guidance:an}:{code:"HTML_RESPONSE",message:`SAP hat eine HTML-Seite statt Daten geliefert: ${ce(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 sn(t.message)?on(t.message)?{code:"AUTH_FAILED",message:`Anmeldung abgelehnt (SAP-Anmeldeseite: ${ce(t.message)}).`,guidance:an}:{code:"HTML_RESPONSE",message:`SAP hat eine HTML-Seite statt Daten geliefert: ${ce(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 Hi(t,e,n){let r=typeof e=="string"?e:JSON.stringify(e??""),s=$i(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(Mi(r))return {code:"MEDIA_OBJECT_MISMATCH",httpStatus:t,...s?{sapCode:s}:{},message:sr(e)??"SAP meldet fehlende Berechtigung f\xFCr das Objekt \u2014 meist ist es aber der Objektschl\xFCssel oder der Objekttyp.",guidance:Ui};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:sr(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(sn(e))return on(e)?{code:"AUTH_FAILED",httpStatus:t,message:`Anmeldung abgelehnt (SAP-Anmeldeseite, HTTP ${t}: ${ce(e)}).`,guidance:an}:{code:"HTML_RESPONSE",httpStatus:t,message:`SAP hat eine HTML-Fehlerseite geliefert (HTTP ${t}): ${ce(e)}`,guidance:"Der Pfad wird von SAP nicht als OData-Service beantwortet. Bei Katalogpfaden sap_discover_services statt sap_query verwenden."};let i=Se(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 sr(t){let e=Se(t);if(e.message&&!(e.message==="Unknown error"||e.message==="Unknown SAP error"))return e.message}var Wi=5e3;function ir(t){let e=t?.issuer?.CN??t?.issuer?.O;return Array.isArray(e)?e.join(", ")||void 0:typeof e=="string"?e:void 0}function O(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 Gi(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 qi(t,e){let n=Date.now();try{let{address:r,family:s}=await e(t);return O("dns",n,"ok",`${t} l\xF6st auf ${r} auf.`,{info:{address:r,family:s}})}catch(r){let s=be(r);return O("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 Ki(t,e,n,r,s){let i=Date.now(),o=a=>new Promise((d,l)=>{let c=s({host:t,port:e,rejectUnauthorized:a,servername:t},()=>{let u=c.getPeerCertificate(true);c.destroy(),d(u);});c.setTimeout(r,()=>{c.destroy(),l(Object.assign(new Error(`TLS-Handshake nach ${r} ms ohne Antwort`),{code:"ETIMEDOUT"}));}),c.on("error",u=>{c.destroy(),l(u);});});try{let a=await o(n),d=ir(a);return O("tls",i,"ok",n?`TLS-Verbindung steht, Zertifikatskette ist verifizierbar.${d?` Aussteller: ${d}.`:""}`:`TLS-Verbindung steht. Die Zertifikatspr\xFCfung ist \xFCber SAP_TLS_VERIFY=false abgeschaltet \u2014 die Kette wurde NICHT verifiziert.${d?` Aussteller: ${d}.`:""}`,{info:{verified:n,...d?{issuer:d}:{},...a?.subject?.CN?{subject:String(a.subject.CN)}:{},...a?.valid_to?{validTo:a.valid_to}:{}}})}catch(a){let d=be(a);if(d.code==="TLS_CHAIN_INCOMPLETE"){let l;try{let c=await o(!1);l=ir(c);}catch{}return O("tls",i,"failed",l?`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar. Aussteller des Serverzertifikats: ${l}.`:"Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar.",{guidance:d.guidance,...l?{info:{issuer:l}}:{}})}return O("tls",i,"failed",d.message,{guidance:d.guidance})}}async function zi(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 [O("auth",n,"ok","Anmeldung am Gateway erfolgreich."),O("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}}),O("catalog",n,"ok",`Servicekatalog erreichbar (HTTP ${r.status}).`)]}catch(r){let s=be(r),i=s.httpStatus===401||s.code==="AUTH_FAILED";return i&&!e?[O("auth",n,"ok","Zugangsdaten wurden gesendet."),O("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."}),O("catalog",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor.")]:i?[O("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.`}),O("client",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor."),O("catalog",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor.")]:[O("auth",n,"ok","Anmeldung am Gateway erfolgreich."),O("client",n,"ok",e?`Mandant ${e} angenommen.`:"Kein Mandant konfiguriert."),O("catalog",n,"failed",s.message,{guidance:s.guidance??"Katalogdienst in /IWFND/MAINT_SERVICE pr\xFCfen. Der Server probiert drei bekannte Katalogpfade durch."})]}}async function pt(t,e){let n=Date.now(),r=e.budgetMs??Wi,s=e.deps?.lookup??lookup,i=e.deps?.connectTls??connect,o=[],a=Gi(e.baseUrl);if(!a)return o.push(O("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(d=>O(d,n,"skipped","Nicht gepr\xFCft \u2014 die Basis-URL ist unbrauchbar."))),ut(o,n);if(o.push(await qi(a.host,s)),o[0].status==="failed")return o.push(...["tls","auth","client","catalog"].map(d=>O(d,n,"skipped","Nicht gepr\xFCft \u2014 der Name war nicht aufl\xF6sbar."))),ut(o,n);if(a.isTls){let d=Math.max(500,r-(Date.now()-n));if(o.push(await Ki(a.host,a.port,e.tlsVerify!==false,d,i)),o[o.length-1].status==="failed")return o.push(...["auth","client","catalog"].map(l=>O(l,n,"skipped","Nicht gepr\xFCft \u2014 die TLS-Stufe scheiterte zuvor."))),ut(o,n)}else o.push(O("tls",n,"skipped","Nicht gepr\xFCft \u2014 SAP_BASE_URL verwendet http, nicht https."));return o.push(...await zi(t,e.sapClient)),ut(o,n)}function ut(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 we=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 dn.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 cn(t){return createHash("sha256").update(t).digest("hex")}var Be=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});}},mt=class{clientTokenManager;cache;config;constructor(e){this.config=e,this.clientTokenManager=new we(e.tokenServiceUrl,e.clientId,e.clientSecret),this.cache=new Be;}async getToken(e){if(!e)return this.clientTokenManager.getToken();let n=cn(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 dn.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 d=parseInt(a.expires_in,10),l=Number.isFinite(d)&&d>0?d:3600,c=Date.now()+(l-60)*1e3;return this.cache.set(n,a.value,c),a.value}};var Fe=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 dn.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 de=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 dn.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 oo={email:"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",sapUsername:"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"};function or(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 ao(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 co(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 ft=class{cache;config;constructor(e){or(e.privateKey,"private key"),or(e.certificate,"certificate"),this.config={...e,clockSkew:e.clockSkew??120,assertionLifetime:e.assertionLifetime??300},this.cache=new Be;}async getToken(e){if(!e)throw new Error("saml-bearer auth requires a user token for principal propagation");let n=cn(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=oo[this.config.nameIdFormat],a=new Date(s-this.config.clockSkew*1e3).toISOString(),d=new Date(s+this.config.assertionLifetime*1e3).toISOString(),l=new Date(s).toISOString(),c=ao({assertionId:`_${randomUUID()}`,issuer:this.config.issuer,nameId:i,nameIdFormat:o,recipient:this.config.tokenServiceUrl,audience:this.config.clientId,notBefore:a,notOnOrAfter:d,authnInstant:l}),u=co(c,this.config.privateKey,this.config.certificate),p=Buffer.from(u).toString("base64"),m=await dn.post(this.config.tokenServiceUrl,new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:saml2-bearer",assertion:p,client_id:this.config.clientId}).toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}}),{access_token:f,expires_in:g}=m.data,h=typeof g=="number"&&g>0?g:3600,b=Date.now()+(h-60)*1e3;return this.cache.set(n,f,b),f}};var lo="[geschwaerzt]",uo=new Set(["accept","accept-encoding","content-type","content-length","if-match","if-none-match","x-http-method","x-requested-with"]);function ar(t){let e="'(?:[^']|'')*'",n=new RegExp(`\\b(guid|datetimeoffset|datetime|time|binary|X)${e}|${e}|\\b\\d+(?:\\.\\d+)?[dfmDFM]?\\b`,"gi");return t.replace(n,(r,s)=>s?`${s}'<${s}>'`:r.startsWith("'")?"'<str>'":"<num>")}function po(t){return t.replace(/\(([^)]*)\)/g,(e,n)=>`(${ar(n)})`)}function mo(t){if(t===null||typeof t!="object")return t;let e={};for(let[n,r]of Object.entries(t))e[n]=uo.has(n.toLowerCase())?r:lo;return e}var gt={paths:["headers","url",'params["$filter"]','params["$search"]'],censor:(t,e)=>{let n=e[e.length-1];return n==="headers"?mo(t):typeof t!="string"?t:n==="url"?po(t):ar(t)}};function cr(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 ho=3e4,ee=go({name:"sap-http-client",redact:gt,transport:{target:"pino/file",options:{destination:2}}});function yo(t,e){return typeof t=="string"?t.trimStart().startsWith("<!DOCTYPE"):Buffer.isBuffer(t)?String(e?.["content-type"]??"").includes("text/html")?true:t.subarray(0,32).toString("latin1").trimStart().startsWith("<!DOCTYPE"):false}function So(t){return t.startsWith("http://")||t.startsWith("https://")}function bo(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 ve=class{constructor(e,n={}){this.destination=e;switch(this.httpsAgent=cr(n),this.requestTimeoutMs=n.requestTimeoutMs??ho,e.auth.type){case "oauth2":this.tokenManager=new we(e.auth.tokenServiceUrl,e.auth.clientId,e.auth.clientSecret);break;case "ias":this.tokenManager=new Fe({issuerUrl:e.auth.tenantUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new de);break;case "xsuaa":this.tokenManager=new Fe({issuerUrl:e.auth.xsuaaUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new de);break;case "btp-principal":this.tokenManager=new mt({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 ft({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){lt();let n=e.method!=="get",r=ye(),s=at();tr(e.method);let 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),d=this.destination.headers??{},l={};if(n){let p=await this.fetchCsrfToken({...d,...a},e.url);p.token&&(l={"x-csrf-token":p.token},p.cookies&&(l.Cookie=p.cookies));}let c={...d,...a,...l,...e.headers},u={method:e.method,url:this.resolveUrl(e.url),data:e.data,headers:c,params:Object.keys(i).length>0?i:void 0,...e.responseType?{responseType:e.responseType}:{},httpsAgent:this.httpsAgent,timeout:this.effectiveTimeoutMs(),signal:r?.signal,beforeRedirect:p=>{this.httpsAgent&&(p.agent=this.httpsAgent);let m=p.headers??{};for(let[f,g]of Object.entries(c))m[f]||(m[f]=g);}};ee.info({...s?{correlationId:s,tool:r?.toolName}:{},...r?.traceId?{traceId:r.traceId}:{},method:u.method,url:u.url,headers:u.headers,params:u.params,hasAuth:!!u.headers.Authorization||!!u.headers.APIKey},"SAP HTTP request outgoing");try{let p=await dn(u);if(ee.info({...s?{correlationId:s}:{},status:p.status,contentType:p.headers["content-type"]},"SAP HTTP response OK"),yo(p.data,p.headers))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 m=e.expectedStatuses?.includes(p.response.status)??false;(m?ee.info.bind(ee):ee.error.bind(ee))({...s?{correlationId:s}:{},status:p.response.status,statusText:p.response.statusText,contentType:p.response.headers?.["content-type"],url:u.url,...m?{expected:true}:{},dataType:typeof p.response.data,dataLength:typeof p.response.data=="string"?p.response.data.length:void 0},m?"SAP HTTP response (expected)":"SAP HTTP response ERROR");}else p instanceof Error&&ee.error({message:p.message,name:p.name},"SAP HTTP connection error");if(n&&this.isCsrf403(p)){lt();try{let m=await this.fetchCsrfToken({...d,...a},e.url),f={...u.headers};m.token&&(f["x-csrf-token"]=m.token,m.cookies&&(f.Cookie=m.cookies)),at();let g=await dn({...u,headers:f});return {data:g.data,headers:g.headers,status:g.status}}catch(m){throw this.toSapError(m)}}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(So(e))return e;let{origin:n,path:r}=bo(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){lt(),at();try{let o={...e,"x-csrf-token":"fetch"},a={};this.destination.sapClient&&(a["sap-client"]=this.destination.sapClient);let d=await dn.get(i,{headers:o,params:Object.keys(a).length>0?a:void 0,validateStatus:()=>!0,timeout:this.effectiveTimeoutMs(),signal:ye()?.signal,httpsAgent:this.httpsAgent,beforeRedirect:c=>{this.httpsAgent&&(c.agent=this.httpsAgent);let u=c.headers??{};for(let[p,m]of Object.entries(o))u[p]||(u[p]=m);}}),l=d.headers["x-csrf-token"];if(ee.debug({url:i,status:d.status,hasToken:!!l},"CSRF token fetch result"),l&&l.toLowerCase()!=="required"){let u=(d.headers["set-cookie"]??[]).map(p=>p.split(";")[0]).join("; ");return {token:l,cookies:u}}}catch{}}return {token:"",cookies:""}}isCsrf403(e){return isAxiosError(e)&&e.response?.status===403}effectiveTimeoutMs(){let e=nr();return e===void 0?this.requestTimeoutMs:Math.max(1,Math.min(this.requestTimeoutMs,e))}toSapError(e){if(e instanceof F)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=ye()?.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?Se(e.response.data):e instanceof Error?{code:"CONNECTION_ERROR",message:e.message,details:[],rawResponse:void 0}:Se(e)}};function dr(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 lr(t,e,n,r){try{let s=new ve(ot(e),n),i=await pt(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 le(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}),je=(t,e,n=15,r=3)=>({name:t,type:"Edm.Decimal",precision:n,scale:r,...e?{label:e}:{}}),ue=(t,e)=>({name:t,type:"Edm.DateTime",precision:7,...e?{label:e}:{}}),ur=(t,e)=>({name:t,type:"Edm.Boolean",...e?{label:e}:{}}),pn="/sap/opu/odata/sap",wo={technicalName:"API_BUSINESS_PARTNER",title:"Business Partner (A2X)",description:"Business partners, customers and suppliers with their addresses.",path:`${pn}/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"),ue("CreationDate","Created On"),ur("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),ue("ValidityStartDate","Valid From"),ue("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:[]},vo={technicalName:"API_SALES_ORDER_SRV",title:"Sales Order (A2X)",description:"Sales orders with items \u2014 read, create (deep insert), change, release.",path:`${pn}/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"),ue("CreationDate","Created On"),ue("LastChangeDateTime","Last Changed"),ue("RequestedDeliveryDate","Requested Delivery Date"),je("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"),je("RequestedQuantity","Order Quantity",15,3),v("RequestedQuantityUnit",3,"Unit"),je("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"}]},To={technicalName:"API_PRODUCT_SRV",title:"Product Master (A2X)",description:"Products (materials) with descriptions.",path:`${pn}/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"),je("GrossWeight","Gross Weight",13,3),je("NetWeight","Net Weight",13,3),v("WeightUnit",3,"Weight Unit"),v("CreatedByUser",12,"Created By"),ue("CreationDate","Created On"),ur("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:[]},ht=[wo,vo,To],ln="MCP_DEMO",Ao=[["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"]],Eo=[["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]],Co=[["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 un(t,e=3){return t.toFixed(e)}function mn(){let t=[],e=[];for(let[d,l,c,u,p,m,f,g,h,b,y,E]of Ao){let S=l==="1",T=S?`${c} ${u}`:c;t.push({BusinessPartner:d,Customer:b==="C"||b==="B"?d:"",Supplier:b==="S"||b==="B"?d:"",BusinessPartnerCategory:l,BusinessPartnerFullName:T,BusinessPartnerName:T,OrganizationBPName1:S?"":c,FirstName:S?c:"",LastName:S?u:"",BusinessPartnerGrouping:S?"BP02":"BP01",SearchTerm1:(S?u:c.split(" ")[0]).toUpperCase().slice(0,20),Industry:y,CreatedByUser:ln,CreationDate:le(E),IsMarkedForArchiving:false}),e.push({BusinessPartner:d,AddressID:String(2e4+t.length),ValidityStartDate:le(E),ValidityEndDate:le("9999-12-31"),StreetName:p,HouseNumber:m,PostalCode:f,CityName:g,Country:"DE",Region:h,Language:"DE"});}let n=[],r=[],s=new Map,i=new Map;Eo.forEach(([d,l,c,u,p,m,f,g,h],b)=>{s.set(d,h),i.set(d,g),n.push({Product:d,ProductType:l,ProductGroup:c,BaseUnit:u,GrossWeight:p,NetWeight:m,WeightUnit:"KG",CreatedByUser:ln,CreationDate:le(`2023-11-${String(6+b).padStart(2,"0")}`),IsMarkedForDeletion:false}),r.push({Product:d,Language:"DE",ProductDescription:f}),r.push({Product:d,Language:"EN",ProductDescription:g});});let o=[],a=[];for(let[d,l,c,u,p,m,f,g]of Co){let h=0;g.forEach(([b,y],E)=>{let S=y*(s.get(b)??0);h+=S,a.push({SalesOrder:d,SalesOrderItem:String((E+1)*10),Material:b,SalesOrderItemText:i.get(b)??b,SalesOrderItemCategory:"TAN",RequestedQuantity:un(y),RequestedQuantityUnit:"ST",NetAmount:un(S),TransactionCurrency:"EUR",ProductionPlant:"1010"});}),o.push({SalesOrder:d,SalesOrderType:"OR",SalesOrganization:"1010",DistributionChannel:"10",OrganizationDivision:"00",SoldToParty:l,PurchaseOrderByCustomer:c,CreatedByUser:ln,CreationDate:le(u),LastChangeDateTime:le(`${u}T09:30:00Z`),RequestedDeliveryDate:le(p),TotalNetAmount:un(h),TransactionCurrency:"EUR",OverallSDProcessStatus:m,OverallDeliveryStatus:f});}return {A_BusinessPartner:t,A_BusinessPartnerAddress:e,A_SalesOrder:o,A_SalesOrderItem:a,A_Product:n,A_ProductDescription:r}}function Ro(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function Po(t,e){return `assoc_${t.set}_${e}`}function pr(t){let e=t.namespace,n=[],r=[],s=[];for(let a of t.entityTypes){let d=a.properties.map(c=>{let u=[`Name="${c.name}"`,`Type="${c.type}"`,`Nullable="${c.nullable===false||a.keys.includes(c.name)?"false":"true"}"`];return c.maxLength!==void 0&&u.push(`MaxLength="${c.maxLength}"`),c.precision!==void 0&&u.push(`Precision="${c.precision}"`),c.scale!==void 0&&u.push(`Scale="${c.scale}"`),c.label&&u.push(`sap:label="${Ro(c.label)}"`),a.keys.includes(c.name)&&u.push('sap:creatable="true" sap:updatable="false"'),` <Property ${u.join(" ")}/>`}).join(`
|
|
5
|
+
`),l=a.navigations.map(c=>{let u=Po(a,c.name),p=`FromRole_${u}`,m=`ToRole_${u}`;return r.push(` <Association Name="${u}" sap:content-version="1">
|
|
6
6
|
<End Type="${e}.${a.set}Type" Multiplicity="1" Role="${p}"/>
|
|
7
7
|
<End Type="${e}.${c.target}Type" Multiplicity="${c.multiplicity}" Role="${m}"/>
|
|
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,9 +38,9 @@ ${o}`:""}
|
|
|
38
38
|
</Schema>
|
|
39
39
|
</edmx:DataServices>
|
|
40
40
|
</edmx:Edmx>
|
|
41
|
-
`}var M=class extends Error{constructor(e){super(e),this.name="ODataSyntaxError";}},pr=new Set(["and","or","not","eq","ne","gt","ge","lt","le"]);function Eo(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}=mr(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 M(`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}=mr(t,n);n=a;let d=i.toLowerCase();if(d==="datetime"||d==="datetimeoffset"){let l=gr(o);if(Number.isNaN(l))throw new M(`Ung\xFCltiges datetime-Literal '${o}'.`);e.push({kind:"datetime",value:l});}else if(d==="guid"||d==="x"||d==="binary"||d==="time")e.push({kind:"string",value:o});else throw new M(`Unbekanntes Literal-Pr\xE4fix '${i}'.`);continue}e.push({kind:"ident",value:i});continue}throw new M(`Unerwartetes Zeichen '${r}' an Position ${n}.`)}return e}function mr(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 M("Zeichenkette ohne schlie\xDFendes Apostroph.")}function gr(t){let e=/[zZ]|[+-]\d{2}:\d{2}$/.test(t)?t:`${t}Z`;return Date.parse(e)}var Co=new Set(["substringof","startswith","endswith","tolower","toupper"]),fn=class{constructor(e){this.tokens=e;}pos=0;parse(){if(this.tokens.length===0)throw new M("Leerer $filter-Ausdruck.");let e=this.parseOr();if(this.pos<this.tokens.length)throw new M(`Unerwartetes Token nach dem Ausdruck: ${fr(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(!pr.has(r))throw new M(`Unbekannter Operator '${n.value}'.`)}return e}parsePrimary(){let e=this.peek();if(!e)throw new M("Ausdruck endet unerwartet.");if(e.kind==="lparen"){this.pos++;let n=this.parseOr();if(this.peek()?.kind!=="rparen")throw new M("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(pr.has(n))throw new M(`Operand erwartet, aber '${e.value}' gefunden.`);if(this.pos++,this.peek()?.kind==="lparen"){if(!Co.has(n))throw new M(`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 M(`Schlie\xDFende Klammer nach ${e.value}(...) fehlt.`);return this.pos++,{type:"call",fn:n,args:r}}return {type:"field",path:e.value}}throw new M(`Unerwartetes Token ${fr(e)}.`)}};function fr(t){if(!t)return "(Ende)";switch(t.kind){case "lparen":return "'('";case "rparen":return "')'";case "comma":return "','";default:return `'${String(t.value)}'`}}var Ro=/^\/Date\((-?\d+)(?:[+-]\d{4})?\)\/$/;function Po(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 Y(t,e){switch(t.type){case "and":return ve(Y(t.left,e))&&ve(Y(t.right,e));case "or":return ve(Y(t.left,e))||ve(Y(t.right,e));case "not":return !ve(Y(t.operand,e));case "literal":return t.datetime?{datetime:t.value}:t.value;case "field":return Po(e,t.path);case "call":{let n=t.args.map(s=>Y(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 M(`Unbekannte Funktion '${t.fn}'.`)}}case "compare":{let[n,r]=ko(Y(t.left,e),Y(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 ve(t){return t===true}function ko(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=Ro.exec(o);if(a)return Number(a[1]);let d=gr(o);return Number.isNaN(d)?null:d}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 hr(t){let e=new fn(Eo(t)).parse();return n=>ve(Y(e,n))}var Ae=class extends Error{constructor(e){super(e),this.name="ODataUrlError";}};function yt(t){let e=t.trim();if(e.startsWith("'"))return _o(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 _o(t){if(!t.endsWith("'")||t.length<2)throw new Ae(`Zeichenkette ohne schlie\xDFendes Apostroph: ${t}`);return t.slice(1,-1).replace(/''/g,"'")}function Oo(t){let e=gn(t);return No(e,",").map(r=>{let s=Do(r,"=");return s===-1?{value:yt(r)}:{name:r.slice(0,s).trim(),value:yt(r.slice(s+1))}})}function hn(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=Io(e,n);i=Oo(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=>gn(l)).filter(l=>l.length>0),d={entitySet:gn(s),segments:a};return i&&(d.key=i),d}function Io(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 Ae(`Schlie\xDFende Klammer fehlt in: ${t}`)}function No(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 Do(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 gn(t){try{return decodeURIComponent(t)}catch{return t}}var St="DEMO",bn="demo",wn="100",Ve="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2",xo="/sap/opu/odata4/",w=class extends Error{constructor(n,r,s,i={}){super(s);this.status=n;this.code=r;this.headers=i;}},bt=class{constructor(e={}){this.options=e;for(let n of ht)for(let r of n.entityTypes)this.types.set(r.set,{type:r,service:n});}data=mn();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=mn(),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 d=(e.headers["x-csrf-token"]??"").toLowerCase()==="fetch"?{"x-csrf-token":this.csrfToken,"set-cookie":`${this.cookie}; path=/`}:{},l=r==="HEAD",c=l?"GET":r,u=c==="POST"&&(e.headers["x-http-method"]??"").toUpperCase()==="MERGE"?"MERGE":c;if(u!=="GET"&&!n.inBatch&&e.headers["x-csrf-token"]!==this.csrfToken)throw new w(403,"HTTP/403","CSRF token validation failed",{"x-csrf-token":"Required"});let m;if(i===Ve||i.startsWith(`${Ve}/`))m=this.handleCatalog(u,i.slice(Ve.length),o,n);else {if(i.startsWith(xo))throw new w(404,"HTTP/404",`Resource not found for segment '${i}'`);{let f=ht.find(g=>i===g.path||i.startsWith(`${g.path}/`));if(!f)throw new w(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${i}'`);m=this.handleService(f,u,i.slice(f.path.length),o,e,n);}}return m.headers={...m.headers,...d},l&&(m.body=""),m}checkAuth(e,n){let r=e.authorization??"",s=`Basic ${Buffer.from(`${this.options.username??St}:${this.options.password??bn}`).toString("base64")}`,i=a=>new w(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??wn))throw i(o?`Mandant ${o} existiert nicht`:"Anmeldung fehlgeschlagen")}handleCatalog(e,n,r,s){if(e!=="GET")throw new w(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=hn(i);if(o.entitySet!=="ServiceCollection")throw new w(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${i}'`);let a=[...ht].map(u=>({__metadata:{id:`${s.base}${Ve}/ServiceCollection('${u.technicalName}_0001')`,uri:`${s.base}${Ve}/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??""),p=a.find(m=>m.ID===u);if(!p)throw new w(404,"/IWFND/CM_MGW/002",`Service '${u}' not found`);return this.json(200,{d:p})}let d=br(r),{rows:l,count:c}=yn(a,d);return this.json(200,{d:{results:l,...d.inlinecount==="allpages"?{__count:String(c)}:{}}})}handleService(e,n,r,s,i,o){let a=r.replace(/^\/+/,"");if(a===""){if(n!=="GET")throw new w(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,ur(e));if(a==="$batch"){if(n!=="POST")throw new w(405,"HTTP/405","Method not allowed on $batch");return this.handleBatch(e,i,o)}let d=e.functionImports.find(l=>l.name===a);if(d){if(n!==d.httpMethod&&!(n==="MERGE"&&d.httpMethod==="POST"))throw new w(405,"HTTP/405",`Function import ${d.name} requires ${d.httpMethod}`);return this.handleFunctionImport(e,d.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 d=r.get(a.name);if(d===null){if(!a.nullable)throw new w(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Parameter '${a.name}' is missing`);continue}o[a.name]=yt(d);}if(n==="ReleaseSalesOrder"){let a=String(o.SalesOrder??""),d=this.rows("A_SalesOrder").find(l=>l.SalesOrder===a);if(!d)throw new w(404,"SD/001",`Sales order ${a} does not exist`);if(d.OverallSDProcessStatus==="C")throw new w(400,"SD/002",`Sales order ${a} is already completed and cannot be released`);return d.OverallSDProcessStatus="B",d.LastChangeDateTime=`/Date(${Date.now()})/`,this.bump("A_SalesOrder",d),this.json(200,{d:this.present(e,this.typeOf("A_SalesOrder"),d,s,{})})}if(n==="GetOpenSalesOrderCount"){let a=o.SoldToParty===void 0?void 0:String(o.SoldToParty),d=this.rows("A_SalesOrder").filter(l=>l.OverallSDProcessStatus!=="C"&&(a===void 0||l.SoldToParty===a)).length;return this.json(200,{d:{GetOpenSalesOrderCount:d}})}throw new w(501,"HTTP/501",`Function import ${n} not implemented`)}handleEntity(e,n,r,s,i,o){let a=i.body,d=i.headers["if-match"],l;try{l=hn(r);}catch(h){throw h instanceof Ae?new w(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR",h.message):h}let c=e.entityTypes.find(h=>h.set===l.entitySet);if(!c)throw new w(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${l.entitySet}'`);let u=br(s),p=this.rows(c.set),m;if(l.key&&(m=this.find(c,l.key),!m))throw new w(404,"/IWBEP/CM_MGW_RT/020",`Resource not found for the segment '${c.set}'`);let f=[...l.segments],g=false;if(f[f.length-1]==="$count"&&(g=true,f.pop()),f.length>0){if(!m)throw new w(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR","Navigation requires a key");if(f.length>1)throw new w(501,"HTTP/501","Only one navigation level is supported");let h=c.navigations.find(E=>E.name===f[0]);if(!h)throw new w(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${f[0]}'`);let b=this.typeOf(h.target),y=this.related(m,h);if(h.multiplicity==="1"){if(m=y[0],!m)throw new w(404,"/IWBEP/CM_MGW_RT/020",`No ${h.target} for this ${c.set}`);c=b,p=[];}else p=y,m=void 0,c=b;}switch(n){case "GET":{if(m){let y=this.json(200,{d:this.present(e,c,m,o,u)});return y.headers.etag=this.etag(c,m),y}if(g){let y=yn(p,{...u,top:void 0,skip:void 0,raw:u.raw});return {status:200,headers:{"content-type":"text/plain"},body:String(y.count)}}let{rows:h,count:b}=yn(p,u);return this.json(200,{d:{results:h.map(y=>this.present(e,c,y,o,u)),...u.inlinecount==="allpages"?{__count:String(b)}:{}}})}case "POST":{if(m||l.key)throw new w(405,"HTTP/405","POST on a single entity is not allowed (use MERGE/PATCH)");if(c.creatable===false)throw new w(405,"HTTP/405",`${c.set} is not creatable`);let h=this.parseBody(a),b=this.create(c,h,o),y=this.json(201,{d:this.present(e,c,b,o,{expand:this.deepExpands(c,h)})});return y.headers.location=this.uri(o,e,c,b),y.headers.etag=this.etag(c,b),y}case "MERGE":case "PATCH":case "PUT":{if(!m)throw new w(405,"HTTP/405",`${n} requires a single entity`);if(c.updatable===false)throw new w(405,"HTTP/405",`${c.set} is not updatable`);this.checkIfMatch(c,m,d);let h=this.parseBody(a);return this.update(c,m,h,n==="PUT"),{status:204,headers:{etag:this.etag(c,m),dataserviceversion:"2.0"},body:""}}case "DELETE":{if(!m)throw new w(405,"HTTP/405","DELETE requires a single entity");if(c.deletable===false)throw new w(405,"HTTP/405",`${c.set} is not deletable`);return this.checkIfMatch(c,m,d),this.remove(c,m),{status:204,headers:{dataserviceversion:"2.0"},body:""}}default:throw new w(405,"HTTP/405",`Method ${n} not allowed`)}}parseBody(e){if(!e||e.trim()==="")throw new w(400,"/IWCOR/CX_DS_BODY_MISSING","Request body is empty");let n;try{n=JSON.parse(e);}catch{throw new w(400,"/IWCOR/CX_DS_JSON_SYNTAX_ERROR","Request body is not valid JSON")}if(n===null||typeof n!="object"||Array.isArray(n))throw new w(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,d]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(d)?d:d&&typeof d=="object"&&Array.isArray(d.results)?d.results:[];o.push({nav:l,children:u});continue}let c=e.properties.find(u=>u.name===a);if(!c)throw new w(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Property '${a}' is invalid`);i[a]=this.coerce(c.name,c.type,d);}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 w(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:d}of o){let l=this.typeOf(a.target),c={};for(let[u,p]of a.join)c[p]=i[u];for(let u of d)this.create(l,u,r,c);}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 d=e.properties.find(l=>l.name===o);if(!d)throw new w(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Property '${o}' is invalid`);if(e.keys.includes(o)&&this.coerce(o,d.type,a)!==n[o])throw new w(400,"/IWBEP/CX_MGW_BUSI_EXCEPTION",`Key property '${o}' cannot be changed`);i[o]=this.coerce(d.name,d.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 w(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 w(400,"/IWCOR/CX_DS_BATCH","multipart/mixed body with boundary expected");let o=`batchresponse_${randomUUID()}`,a=[];for(let d of wr(n.body,i)){let l=/content-type:\s*multipart\/mixed;\s*boundary=("?)([^";\r\n]+)\1/i.exec(d);if(l){let c=l[2],u=d.indexOf(`\r
|
|
41
|
+
`}var M=class extends Error{constructor(e){super(e),this.name="ODataSyntaxError";}},mr=new Set(["and","or","not","eq","ne","gt","ge","lt","le"]);function ko(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}=fr(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 M(`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}=fr(t,n);n=a;let d=i.toLowerCase();if(d==="datetime"||d==="datetimeoffset"){let l=hr(o);if(Number.isNaN(l))throw new M(`Ung\xFCltiges datetime-Literal '${o}'.`);e.push({kind:"datetime",value:l});}else if(d==="guid"||d==="x"||d==="binary"||d==="time")e.push({kind:"string",value:o});else throw new M(`Unbekanntes Literal-Pr\xE4fix '${i}'.`);continue}e.push({kind:"ident",value:i});continue}throw new M(`Unerwartetes Zeichen '${r}' an Position ${n}.`)}return e}function fr(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 M("Zeichenkette ohne schlie\xDFendes Apostroph.")}function hr(t){let e=/[zZ]|[+-]\d{2}:\d{2}$/.test(t)?t:`${t}Z`;return Date.parse(e)}var _o=new Set(["substringof","startswith","endswith","tolower","toupper"]),fn=class{constructor(e){this.tokens=e;}pos=0;parse(){if(this.tokens.length===0)throw new M("Leerer $filter-Ausdruck.");let e=this.parseOr();if(this.pos<this.tokens.length)throw new M(`Unerwartetes Token nach dem Ausdruck: ${gr(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(!mr.has(r))throw new M(`Unbekannter Operator '${n.value}'.`)}return e}parsePrimary(){let e=this.peek();if(!e)throw new M("Ausdruck endet unerwartet.");if(e.kind==="lparen"){this.pos++;let n=this.parseOr();if(this.peek()?.kind!=="rparen")throw new M("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(mr.has(n))throw new M(`Operand erwartet, aber '${e.value}' gefunden.`);if(this.pos++,this.peek()?.kind==="lparen"){if(!_o.has(n))throw new M(`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 M(`Schlie\xDFende Klammer nach ${e.value}(...) fehlt.`);return this.pos++,{type:"call",fn:n,args:r}}return {type:"field",path:e.value}}throw new M(`Unerwartetes Token ${gr(e)}.`)}};function gr(t){if(!t)return "(Ende)";switch(t.kind){case "lparen":return "'('";case "rparen":return "')'";case "comma":return "','";default:return `'${String(t.value)}'`}}var Oo=/^\/Date\((-?\d+)(?:[+-]\d{4})?\)\/$/;function Io(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 X(t,e){switch(t.type){case "and":return Te(X(t.left,e))&&Te(X(t.right,e));case "or":return Te(X(t.left,e))||Te(X(t.right,e));case "not":return !Te(X(t.operand,e));case "literal":return t.datetime?{datetime:t.value}:t.value;case "field":return Io(e,t.path);case "call":{let n=t.args.map(s=>X(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 M(`Unbekannte Funktion '${t.fn}'.`)}}case "compare":{let[n,r]=No(X(t.left,e),X(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 Te(t){return t===true}function No(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=Oo.exec(o);if(a)return Number(a[1]);let d=hr(o);return Number.isNaN(d)?null:d}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 yr(t){let e=new fn(ko(t)).parse();return n=>Te(X(e,n))}var Ae=class extends Error{constructor(e){super(e),this.name="ODataUrlError";}};function yt(t){let e=t.trim();if(e.startsWith("'"))return Do(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 Do(t){if(!t.endsWith("'")||t.length<2)throw new Ae(`Zeichenkette ohne schlie\xDFendes Apostroph: ${t}`);return t.slice(1,-1).replace(/''/g,"'")}function xo(t){let e=gn(t);return Uo(e,",").map(r=>{let s=$o(r,"=");return s===-1?{value:yt(r)}:{name:r.slice(0,s).trim(),value:yt(r.slice(s+1))}})}function hn(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=Mo(e,n);i=xo(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=>gn(l)).filter(l=>l.length>0),d={entitySet:gn(s),segments:a};return i&&(d.key=i),d}function Mo(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 Ae(`Schlie\xDFende Klammer fehlt in: ${t}`)}function Uo(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 $o(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 gn(t){try{return decodeURIComponent(t)}catch{return t}}var St="DEMO",bn="demo",wn="100",Ve="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2",Lo="/sap/opu/odata4/",w=class extends Error{constructor(n,r,s,i={}){super(s);this.status=n;this.code=r;this.headers=i;}},bt=class{constructor(e={}){this.options=e;for(let n of ht)for(let r of n.entityTypes)this.types.set(r.set,{type:r,service:n});}data=mn();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=mn(),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 d=(e.headers["x-csrf-token"]??"").toLowerCase()==="fetch"?{"x-csrf-token":this.csrfToken,"set-cookie":`${this.cookie}; path=/`}:{},l=r==="HEAD",c=l?"GET":r,u=c==="POST"&&(e.headers["x-http-method"]??"").toUpperCase()==="MERGE"?"MERGE":c;if(u!=="GET"&&!n.inBatch&&e.headers["x-csrf-token"]!==this.csrfToken)throw new w(403,"HTTP/403","CSRF token validation failed",{"x-csrf-token":"Required"});let m;if(i===Ve||i.startsWith(`${Ve}/`))m=this.handleCatalog(u,i.slice(Ve.length),o,n);else {if(i.startsWith(Lo))throw new w(404,"HTTP/404",`Resource not found for segment '${i}'`);{let f=ht.find(g=>i===g.path||i.startsWith(`${g.path}/`));if(!f)throw new w(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${i}'`);m=this.handleService(f,u,i.slice(f.path.length),o,e,n);}}return m.headers={...m.headers,...d},l&&(m.body=""),m}checkAuth(e,n){let r=e.authorization??"",s=`Basic ${Buffer.from(`${this.options.username??St}:${this.options.password??bn}`).toString("base64")}`,i=a=>new w(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??wn))throw i(o?`Mandant ${o} existiert nicht`:"Anmeldung fehlgeschlagen")}handleCatalog(e,n,r,s){if(e!=="GET")throw new w(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=hn(i);if(o.entitySet!=="ServiceCollection")throw new w(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${i}'`);let a=[...ht].map(u=>({__metadata:{id:`${s.base}${Ve}/ServiceCollection('${u.technicalName}_0001')`,uri:`${s.base}${Ve}/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??""),p=a.find(m=>m.ID===u);if(!p)throw new w(404,"/IWFND/CM_MGW/002",`Service '${u}' not found`);return this.json(200,{d:p})}let d=wr(r),{rows:l,count:c}=yn(a,d);return this.json(200,{d:{results:l,...d.inlinecount==="allpages"?{__count:String(c)}:{}}})}handleService(e,n,r,s,i,o){let a=r.replace(/^\/+/,"");if(a===""){if(n!=="GET")throw new w(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,pr(e));if(a==="$batch"){if(n!=="POST")throw new w(405,"HTTP/405","Method not allowed on $batch");return this.handleBatch(e,i,o)}let d=e.functionImports.find(l=>l.name===a);if(d){if(n!==d.httpMethod&&!(n==="MERGE"&&d.httpMethod==="POST"))throw new w(405,"HTTP/405",`Function import ${d.name} requires ${d.httpMethod}`);return this.handleFunctionImport(e,d.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 d=r.get(a.name);if(d===null){if(!a.nullable)throw new w(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Parameter '${a.name}' is missing`);continue}o[a.name]=yt(d);}if(n==="ReleaseSalesOrder"){let a=String(o.SalesOrder??""),d=this.rows("A_SalesOrder").find(l=>l.SalesOrder===a);if(!d)throw new w(404,"SD/001",`Sales order ${a} does not exist`);if(d.OverallSDProcessStatus==="C")throw new w(400,"SD/002",`Sales order ${a} is already completed and cannot be released`);return d.OverallSDProcessStatus="B",d.LastChangeDateTime=`/Date(${Date.now()})/`,this.bump("A_SalesOrder",d),this.json(200,{d:this.present(e,this.typeOf("A_SalesOrder"),d,s,{})})}if(n==="GetOpenSalesOrderCount"){let a=o.SoldToParty===void 0?void 0:String(o.SoldToParty),d=this.rows("A_SalesOrder").filter(l=>l.OverallSDProcessStatus!=="C"&&(a===void 0||l.SoldToParty===a)).length;return this.json(200,{d:{GetOpenSalesOrderCount:d}})}throw new w(501,"HTTP/501",`Function import ${n} not implemented`)}handleEntity(e,n,r,s,i,o){let a=i.body,d=i.headers["if-match"],l;try{l=hn(r);}catch(h){throw h instanceof Ae?new w(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR",h.message):h}let c=e.entityTypes.find(h=>h.set===l.entitySet);if(!c)throw new w(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${l.entitySet}'`);let u=wr(s),p=this.rows(c.set),m;if(l.key&&(m=this.find(c,l.key),!m))throw new w(404,"/IWBEP/CM_MGW_RT/020",`Resource not found for the segment '${c.set}'`);let f=[...l.segments],g=false;if(f[f.length-1]==="$count"&&(g=true,f.pop()),f.length>0){if(!m)throw new w(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR","Navigation requires a key");if(f.length>1)throw new w(501,"HTTP/501","Only one navigation level is supported");let h=c.navigations.find(E=>E.name===f[0]);if(!h)throw new w(404,"/IWFND/CM_MGW/002",`Resource not found for the segment '${f[0]}'`);let b=this.typeOf(h.target),y=this.related(m,h);if(h.multiplicity==="1"){if(m=y[0],!m)throw new w(404,"/IWBEP/CM_MGW_RT/020",`No ${h.target} for this ${c.set}`);c=b,p=[];}else p=y,m=void 0,c=b;}switch(n){case "GET":{if(m){let y=this.json(200,{d:this.present(e,c,m,o,u)});return y.headers.etag=this.etag(c,m),y}if(g){let y=yn(p,{...u,top:void 0,skip:void 0,raw:u.raw});return {status:200,headers:{"content-type":"text/plain"},body:String(y.count)}}let{rows:h,count:b}=yn(p,u);return this.json(200,{d:{results:h.map(y=>this.present(e,c,y,o,u)),...u.inlinecount==="allpages"?{__count:String(b)}:{}}})}case "POST":{if(m||l.key)throw new w(405,"HTTP/405","POST on a single entity is not allowed (use MERGE/PATCH)");if(c.creatable===false)throw new w(405,"HTTP/405",`${c.set} is not creatable`);let h=this.parseBody(a),b=this.create(c,h,o),y=this.json(201,{d:this.present(e,c,b,o,{expand:this.deepExpands(c,h)})});return y.headers.location=this.uri(o,e,c,b),y.headers.etag=this.etag(c,b),y}case "MERGE":case "PATCH":case "PUT":{if(!m)throw new w(405,"HTTP/405",`${n} requires a single entity`);if(c.updatable===false)throw new w(405,"HTTP/405",`${c.set} is not updatable`);this.checkIfMatch(c,m,d);let h=this.parseBody(a);return this.update(c,m,h,n==="PUT"),{status:204,headers:{etag:this.etag(c,m),dataserviceversion:"2.0"},body:""}}case "DELETE":{if(!m)throw new w(405,"HTTP/405","DELETE requires a single entity");if(c.deletable===false)throw new w(405,"HTTP/405",`${c.set} is not deletable`);return this.checkIfMatch(c,m,d),this.remove(c,m),{status:204,headers:{dataserviceversion:"2.0"},body:""}}default:throw new w(405,"HTTP/405",`Method ${n} not allowed`)}}parseBody(e){if(!e||e.trim()==="")throw new w(400,"/IWCOR/CX_DS_BODY_MISSING","Request body is empty");let n;try{n=JSON.parse(e);}catch{throw new w(400,"/IWCOR/CX_DS_JSON_SYNTAX_ERROR","Request body is not valid JSON")}if(n===null||typeof n!="object"||Array.isArray(n))throw new w(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,d]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(d)?d:d&&typeof d=="object"&&Array.isArray(d.results)?d.results:[];o.push({nav:l,children:u});continue}let c=e.properties.find(u=>u.name===a);if(!c)throw new w(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Property '${a}' is invalid`);i[a]=this.coerce(c.name,c.type,d);}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 w(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:d}of o){let l=this.typeOf(a.target),c={};for(let[u,p]of a.join)c[p]=i[u];for(let u of d)this.create(l,u,r,c);}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 d=e.properties.find(l=>l.name===o);if(!d)throw new w(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Property '${o}' is invalid`);if(e.keys.includes(o)&&this.coerce(o,d.type,a)!==n[o])throw new w(400,"/IWBEP/CX_MGW_BUSI_EXCEPTION",`Key property '${o}' cannot be changed`);i[o]=this.coerce(d.name,d.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 w(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 w(400,"/IWCOR/CX_DS_BATCH","multipart/mixed body with boundary expected");let o=`batchresponse_${randomUUID()}`,a=[];for(let d of vr(n.body,i)){let l=/content-type:\s*multipart\/mixed;\s*boundary=("?)([^";\r\n]+)\1/i.exec(d);if(l){let c=l[2],u=d.indexOf(`\r
|
|
42
42
|
\r
|
|
43
|
-
`),p=u===-1?"":d.slice(u+4),m=this.snapshot(),f=[],g;for(let h of
|
|
43
|
+
`),p=u===-1?"":d.slice(u+4),m=this.snapshot(),f=[],g;for(let h of vr(p,c)){let b=this.runBatchPart(e,h,r);if(b.status>=400){g=b;break}f.push(b);}if(g)this.restore(m),a.push(`--${o}\r
|
|
44
44
|
${Sn(g)}`);else {let h=`changesetresponse_${randomUUID()}`,b=f.map(y=>`--${h}\r
|
|
45
45
|
${Sn(y)}`);a.push(`--${o}\r
|
|
46
46
|
Content-Type: multipart/mixed; boundary=${h}\r
|
|
@@ -50,19 +50,19 @@ ${b.join(`\r
|
|
|
50
50
|
--${h}--`);}continue}a.push(`--${o}\r
|
|
51
51
|
${Sn(this.runBatchPart(e,d,r))}`);}return a.push(`--${o}--`),{status:202,headers:{"content-type":`multipart/mixed; boundary=${o}`,dataserviceversion:"2.0"},body:`${a.join(`\r
|
|
52
52
|
`)}\r
|
|
53
|
-
`}}runBatchPart(e,n,r){let s=
|
|
53
|
+
`}}runBatchPart(e,n,r){let s=Fo(n);if(!s)return this.errorResponse(new w(400,"/IWCOR/CX_DS_BATCH","Malformed batch part"));let i=/^https?:\/\//.test(s.url)||s.url.startsWith("/")?s.url:`${e.path}/${s.url}`;try{return this.dispatch({method:s.method,url:i,headers:s.headers,body:s.body},{...r,inBatch:!0})}catch(o){return this.errorResponse(o)}}snapshot(){return {data:Object.fromEntries(Object.entries(this.data).map(([e,n])=>[e,n.map(r=>({...r}))])),versions:new Map(this.versions)}}restore(e){this.data=e.data,this.versions=e.versions;}present(e,n,r,s,i){let o=this.uri(s,e,n,r),a={__metadata:{id:o,uri:o,type:`${e.namespace}.${n.set}Type`,etag:this.etag(n,r)}},d=i.select&&i.select.length>0?new Set(i.select.map(c=>c.split("/")[0])):void 0;if(d){for(let c of d)if(!n.properties.some(u=>u.name===c)&&!n.navigations.some(u=>u.name===c))throw new w(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Property '${c}' is invalid for $select`)}for(let c of n.properties)d&&!d.has(c.name)||(a[c.name]=r[c.name]??null);let l=new Set((i.expand??[]).map(c=>c.split("/")[0]));for(let c of l)if(!n.navigations.some(u=>u.name===c))throw new w(400,"/IWBEP/CX_MGW_TECH_EXCEPTION",`Navigation property '${c}' is invalid for $expand`);for(let c of n.navigations)if(!(d&&!d.has(c.name)&&!l.has(c.name)))if(l.has(c.name)){let u=this.typeOf(c.target),p=this.types.get(c.target).service,m=this.related(r,c).map(f=>this.present(p,u,f,s,{}));a[c.name]=c.multiplicity==="*"?{results:m}:m[0]??null;}else a[c.name]={__deferred:{uri:`${o}/${c.name}`}};return a}uri(e,n,r,s){let i=r.keys.length===1?`'${encodeURIComponent(String(s[r.keys[0]]))}'`:r.keys.map(o=>`${o}='${encodeURIComponent(String(s[o]))}'`).join(",");return `${e.base}${n.path}/${r.set}(${i})`}json(e,n){return {status:e,headers:{"content-type":"application/json; charset=utf-8",dataserviceversion:"2.0"},body:JSON.stringify(n)}}xml(e,n){return {status:e,headers:{"content-type":"application/xml",dataserviceversion:"2.0"},body:n}}errorResponse(e){return e instanceof M&&(e=new w(400,"/IWCOR/CX_DS_EXPR_SYNTAX_ERROR",`Invalid $filter: ${e.message}`)),e instanceof w?{status:e.status,headers:{"content-type":"application/json; charset=utf-8",dataserviceversion:"2.0",...e.headers},body:JSON.stringify({error:{code:e.code,message:{lang:"en",value:e.message},innererror:{application:{component_id:"DEMO",service_namespace:"/SAP/",service_id:"MOCK",service_version:"0001"},errordetails:[]}}})}:{status:500,headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify({error:{code:"HTTP/500",message:{lang:"en",value:e instanceof Error?e.message:String(e)}}})}}typeOf(e){let n=this.types.get(e);if(!n)throw new w(404,"/IWFND/CM_MGW/002",`Entity set ${e} unknown`);return n.type}find(e,n){let r={};if(n.length===1&&n[0].name===void 0)r[e.keys[0]]=String(n[0].value);else for(let s of n){if(!s.name||!e.keys.includes(s.name))throw new w(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR",`Invalid key '${s.name??s.value}' for ${e.set}`);r[s.name]=String(s.value);}if(Object.keys(r).length!==e.keys.length)throw new w(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR",`Key of ${e.set} needs ${e.keys.join(", ")}`);return this.rows(e.set).find(s=>e.keys.every(i=>String(s[i])===r[i]))}related(e,n){return this.rows(n.target).filter(r=>n.join.every(([s,i])=>r[i]===e[s]))}nextKey(e,n,r){if(e.set==="A_SalesOrderItem"&&n==="SalesOrderItem"){let l=this.rows(e.set).filter(c=>c.SalesOrder===r.SalesOrder).reduce((c,u)=>Math.max(c,Number(u.SalesOrderItem)||0),0);return String(l+10)}if(e.set==="A_ProductDescription"&&n==="Language")return "EN";let s=e.autoKey;if(!s)throw new w(400,"/IWBEP/CX_MGW_BUSI_EXCEPTION",`Key '${n}' of ${e.set} is required`);let i=this.nextNumber.get(e.set);if(i===void 0){let d=this.rows(e.set).map(l=>String(l[n])).filter(l=>l.startsWith(s.prefix)).map(l=>Number(l.slice(s.prefix.length))).filter(l=>Number.isFinite(l));i=(d.length>0?Math.max(...d):0)+1;}this.nextNumber.set(e.set,i+1);let o=String(i).padStart(Math.max(1,s.width-s.prefix.length),"0"),a=`${s.prefix}${o}`;return this.rows(e.set).some(d=>String(d[n])===a)?this.nextKey(e,n,r):a}coerce(e,n,r){if(r==null)return null;switch(n){case "Edm.Boolean":if(typeof r=="boolean")return r;if(r==="true"||r==="X")return true;if(r==="false"||r==="")return false;throw new w(400,"/IWCOR/CX_DS_JSON_SYNTAX_ERROR",`Property '${e}' expects a boolean`);case "Edm.Decimal":{let s=Number(r);if(typeof r=="boolean"||Number.isNaN(s))throw new w(400,"/IWCOR/CX_DS_JSON_SYNTAX_ERROR",`Property '${e}' expects a decimal number`);return s.toFixed(3)}case "Edm.Int32":{let s=Number(r);if(!Number.isInteger(s))throw new w(400,"/IWCOR/CX_DS_JSON_SYNTAX_ERROR",`Property '${e}' expects an integer`);return s}case "Edm.DateTime":{if(typeof r=="string"&&/^\/Date\(-?\d+/.test(r))return r;let s=typeof r=="number"?r:Date.parse(String(r));if(Number.isNaN(s))throw new w(400,"/IWCOR/CX_DS_JSON_SYNTAX_ERROR",`Property '${e}' expects a date (ISO 8601 or /Date(ms)/)`);return `/Date(${s})/`}default:return String(r)}}emptyValue(e){switch(e){case "Edm.Boolean":return false;case "Edm.Decimal":return "0.000";case "Edm.Int32":return 0;case "Edm.DateTime":return null;default:return ""}}defaults(e,n){let r=`/Date(${Date.now()})/`;if(e.properties.some(s=>s.name==="CreationDate")&&!n.CreationDate&&(n.CreationDate=r),e.properties.some(s=>s.name==="CreatedByUser")&&!n.CreatedByUser&&(n.CreatedByUser=St),e.set==="A_SalesOrder"&&(n.SalesOrderType||="OR",n.SalesOrganization||="1010",n.DistributionChannel||="10",n.OrganizationDivision||="00",n.TransactionCurrency||="EUR",n.OverallSDProcessStatus||="A",n.OverallDeliveryStatus||="A",n.LastChangeDateTime||=r),e.set==="A_SalesOrderItem"&&(n.SalesOrderItemCategory||="TAN",n.RequestedQuantityUnit||="ST",n.TransactionCurrency||="EUR",n.ProductionPlant||="1010",!n.SalesOrderItemText)){let s=this.rows("A_ProductDescription").find(i=>i.Product===n.Material&&i.Language==="EN");n.SalesOrderItemText=s?.ProductDescription??n.Material??"";}if(e.set==="A_BusinessPartner"){n.BusinessPartnerCategory||="2";let s=String(n.BusinessPartnerName||n.OrganizationBPName1||`${n.FirstName??""} ${n.LastName??""}`.trim());n.BusinessPartnerName||=s,n.BusinessPartnerFullName||=s,n.BusinessPartnerGrouping||=n.BusinessPartnerCategory==="1"?"BP02":"BP01";}e.set==="A_BusinessPartnerAddress"&&(n.Country||="DE",n.Language||="DE",n.ValidityStartDate||=r,n.ValidityEndDate||="/Date(253402214400000)/"),e.set==="A_Product"&&(n.ProductType||="HAWA",n.BaseUnit||="ST",n.WeightUnit||="KG");}recalculate(e,n){if(e.set!=="A_SalesOrder")return;let s=this.rows("A_SalesOrderItem").filter(i=>i.SalesOrder===n.SalesOrder).reduce((i,o)=>i+Number(o.NetAmount??0),0);n.TotalNetAmount=s.toFixed(3);}versionKey(e,n){let r=this.typeOf(e);return `${e}|${r.keys.map(s=>String(n[s])).join("|")}`}bump(e,n){let r=this.versionKey(e,n);this.versions.set(r,(this.versions.get(r)??1)+1);}etag(e,n){return `W/"${this.versions.get(this.versionKey(e.set,n))??1}"`}deepExpands(e,n){return e.navigations.filter(r=>r.name in n).map(r=>r.name)}};function wr(t){let e=s=>{let i=t.get(s);if(i===null)return;let o=Number(i);if(!Number.isInteger(o)||o<0)throw new w(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR",`Invalid value '${i}' for ${s}`);return o},n=s=>{let i=t.get(s);if(!(i===null||i.trim()===""))return i.split(",").map(o=>o.trim()).filter(o=>o.length>0)},r=t.get("$inlinecount")??void 0;if(r!==void 0&&r!=="allpages"&&r!=="none")throw new w(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR",`Invalid value '${r}' for $inlinecount`);return {filter:t.get("$filter")??void 0,orderby:t.get("$orderby")??void 0,top:e("$top"),skip:e("$skip"),select:n("$select"),expand:n("$expand"),inlinecount:r,raw:t}}function yn(t,e){let n=t;if(e.filter&&(n=n.filter(yr(e.filter))),e.orderby){let o=e.orderby.split(",").map(a=>{let[d,l]=a.trim().split(/\s+/);if(!d)throw new w(400,"/IWCOR/CX_DS_URI_SYNTAX_ERROR","Empty $orderby clause");return {field:d,desc:(l??"asc").toLowerCase()==="desc"}});n=[...n].sort((a,d)=>{for(let{field:l,desc:c}of o){let u=Bo(a[l],d[l]);if(u!==0)return c?-u:u}return 0});}let r=n.length,s=e.skip??0,i=e.top;return n=n.slice(s,i===void 0?void 0:s+i),{rows:n,count:r}}function Bo(t,e){if(t===e)return 0;if(t==null)return -1;if(e==null)return 1;let n=Number(t),r=Number(e),s=typeof t=="string"?/^\/Date\((-?\d+)/.exec(t)?.[1]:void 0,i=typeof e=="string"?/^\/Date\((-?\d+)/.exec(e)?.[1]:void 0;return s!==void 0&&i!==void 0?Number(s)-Number(i):!Number.isNaN(n)&&!Number.isNaN(r)&&String(t).trim()!==""&&String(e).trim()!==""?n-r:String(t)<String(e)?-1:1}function vr(t,e){let n=[],r=`--${e}`,s=t.split(r);for(let i=1;i<s.length;i++){let o=s[i];if(o.startsWith("--"))break;o=o.replace(/^\r?\n/,"").replace(/\r?\n$/,""),o.trim().length>0&&n.push(o);}return n}function Fo(t){let e=t.replace(/\r\n/g,`
|
|
54
54
|
`),n=/^(GET|POST|PUT|PATCH|DELETE|MERGE|HEAD)\s+(\S+)\s+HTTP\/1\.\d\s*$/im.exec(e);if(!n)return;let r=e.slice(n.index+n[0].length).replace(/^\n/,""),s=r.indexOf(`
|
|
55
55
|
|
|
56
56
|
`),i=s===-1?r:r.slice(0,s),o=s===-1?void 0:r.slice(s+2).trim()||void 0,a={};for(let d of i.split(`
|
|
57
|
-
`)){let l=d.indexOf(":");l!==-1&&(a[d.slice(0,l).trim().toLowerCase()]=d.slice(l+1).trim());}return {method:n[1].toUpperCase(),url:n[2],headers:a,body:o}}var
|
|
58
|
-
`)}function Tr(t){return {name:"default",baseUrl:t,authType:"basic",username:St,password:bn,sapClient:wn}}async function
|
|
57
|
+
`)){let l=d.indexOf(":");l!==-1&&(a[d.slice(0,l).trim().toLowerCase()]=d.slice(l+1).trim());}return {method:n[1].toUpperCase(),url:n[2],headers:a,body:o}}var Ho={200:"OK",201:"Created",202:"Accepted",204:"No Content",400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",412:"Precondition Failed",500:"Internal Server Error",501:"Not Implemented"};function Sn(t){let e=["Content-Type: application/http","Content-Transfer-Encoding: binary","",`HTTP/1.1 ${t.status} ${Ho[t.status]??""}`.trimEnd()];for(let[n,r]of Object.entries(t.headers))e.push(`${n}: ${r}`);return e.push(`Content-Length: ${Buffer.byteLength(t.body,"utf8")}`),e.push(""),e.push(t.body),e.join(`\r
|
|
58
|
+
`)}function Tr(t){return {name:"default",baseUrl:t,authType:"basic",username:St,password:bn,sapClient:wn}}async function Ar(t={}){let e=new bt,n=createServer((i,o)=>{let a=[];i.on("data",d=>a.push(d)),i.on("end",()=>{let d={};for(let[c,u]of Object.entries(i.headers))d[c.toLowerCase()]=Array.isArray(u)?u.join(", "):u;let l=e.handle({method:i.method??"GET",url:i.url??"/",headers:d,body:a.length>0?Buffer.concat(a).toString("utf8"):void 0});o.writeHead(l.status,l.headers),o.end(l.body);});});await new Promise((i,o)=>{n.once("error",o),n.listen(t.port??0,"127.0.0.1",()=>i());});let r=n.address(),s=typeof r=="object"&&r!==null?r.port:t.port??0;return {baseUrl:`http://127.0.0.1:${s}`,port:s,mock:e,close:()=>new Promise((i,o)=>{n.close(a=>a?o(a):i());})}}var te=z$2.object({name:z$2.string().default("default"),baseUrl:z$2.url(),sapClient:z$2.string().optional()}),Vo=te.extend({authType:z$2.literal("basic"),username:z$2.string().min(1),password:z$2.string().min(1)}),Wo=te.extend({authType:z$2.literal("user-basic")}),Go=te.extend({authType:z$2.literal("oauth2"),clientId:z$2.string().min(1),clientSecret:z$2.string().min(1),tokenServiceUrl:z$2.url()}),qo=te.extend({authType:z$2.literal("apikey"),apiKey:z$2.string().min(1)}),Ko=te.extend({authType:z$2.literal("ias"),tenantUrl:z$2.url(),clientId:z$2.string().min(1),clientSecret:z$2.string().min(1)}),zo=te.extend({authType:z$2.literal("xsuaa"),xsuaaUrl:z$2.url(),clientId:z$2.string().min(1),clientSecret:z$2.string().min(1)}),Yo=te.extend({authType:z$2.literal("btp-principal"),destinationName:z$2.string().min(1),destinationServiceUrl:z$2.url(),clientId:z$2.string().min(1),clientSecret:z$2.string().min(1),tokenServiceUrl:z$2.url()}),Xo=te.extend({authType:z$2.literal("saml-bearer"),tokenServiceUrl:z$2.url(),clientId:z$2.string().min(1),issuer:z$2.string().min(1),nameIdFormat:z$2.enum(["email","sapUsername"]).default("email"),privateKey:z$2.string().min(1),certificate:z$2.string().min(1),clockSkew:z$2.coerce.number().int().nonnegative().default(120),assertionLifetime:z$2.coerce.number().int().positive().default(300)}),Ee=z$2.discriminatedUnion("authType",[Vo,Wo,Go,qo,Ko,zo,Yo,Xo]);function We(t=process.env){let e={name:t.SAP_CONNECTION_NAME??"default",baseUrl:t.SAP_BASE_URL,authType:t.SAP_AUTH_TYPE?.toLowerCase(),sapClient:t.SAP_CLIENT,username:t.SAP_USERNAME,password:t.SAP_PASSWORD,clientId:t.SAP_CLIENT_ID,clientSecret:t.SAP_CLIENT_SECRET,tokenServiceUrl:t.SAP_TOKEN_SERVICE_URL,apiKey:t.SAP_API_KEY,tenantUrl:t.SAP_TENANT_URL,xsuaaUrl:t.SAP_XSUAA_URL,destinationName:t.SAP_DESTINATION_NAME,destinationServiceUrl:t.SAP_DESTINATION_SERVICE_URL,issuer:t.SAP_ISSUER,nameIdFormat:t.SAP_NAME_ID_FORMAT,privateKey:t.SAP_PRIVATE_KEY,certificate:t.SAP_CERTIFICATE,clockSkew:t.SAP_CLOCK_SKEW,assertionLifetime:t.SAP_ASSERTION_LIFETIME},n=Object.fromEntries(Object.entries(e).filter(([s,i])=>i!==void 0)),r=Ee.safeParse(n);if(!r.success){let s=r.error.issues.map(i=>` - ${i.path.join(".")||"root"}: ${i.message}`).join(`
|
|
59
59
|
`);throw new Error(`Invalid SAP connection configuration:
|
|
60
60
|
${s}
|
|
61
61
|
|
|
62
|
-
Set the required SAP_* environment variables.`)}return r.data}var
|
|
62
|
+
Set the required SAP_* environment variables.`)}return r.data}var vn=/^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/,Jo=z$2.enum(["core","odata","idoc","rfc"]),Zo=z$2.object({readOnly:z$2.boolean().optional(),tiers:z$2.array(Jo).min(1).optional()}).strict(),Qo=z$2.object({hash:z$2.string().regex(/^sha256:[0-9a-f]{64}$/,"muss die Form sha256:<64 hex> haben"),destination:z$2.string().min(1),label:z$2.string().max(200).optional(),policy:Zo.optional(),createdAt:z$2.string().optional(),revoked:z$2.boolean().optional()}).strict(),ea=z$2.object({version:z$2.literal(1),destinations:z$2.record(z$2.string(),z$2.unknown()),tokens:z$2.array(Qo).default([])}).strict(),Er=/\$\{([A-Z_][A-Z0-9_]*)\}/g;function Tn(t,e){let n=[],r=s=>{if(typeof s=="string"){let i=[...s.matchAll(Er)].map(a=>a[1]);if(i.length===0)return s;let o=i.filter(a=>e[a]===void 0);if(o.length>0){for(let a of o)n.includes(a)||n.push(a);return s}return s.replace(Er,(a,d)=>e[d]??"")}return Array.isArray(s)?s.map(r):s!==null&&typeof s=="object"?Object.fromEntries(Object.entries(s).map(([i,o])=>[i,r(o)])):s};return {value:r(t),missing:n}}var ta=Ee.options.map(t=>t.shape.authType.value);function Cr(t,e){return e.map(n=>{let r=n.path.join("."),s=r==="authType"?`fehlt oder ist unbekannt (erlaubt: ${ta.join(", ")})`:n.message;return `${t}${r?`${r}: `:""}${s}`})}function wt(t,e){if(t===null||typeof t!="object"||Array.isArray(t))return {ok:false,errors:['Die Datei muss ein JSON-Objekt mit "version" und "destinations" sein.']};let n=ea.safeParse(t);if(!n.success)return {ok:false,errors:Cr("",n.error.issues)};let r=[],s=new Map,i=Object.entries(n.data.destinations);i.length===0&&r.push('"destinations" enth\xE4lt keinen Eintrag.');for(let[a,d]of i){if(!vn.test(a)){r.push(`Destination "${a}": unzul\xE4ssiger Name \u2014 erlaubt sind Buchstaben, Ziffern, "_", "." und "-" (max. 64 Zeichen, kein Leerzeichen).`);continue}if(d===null||typeof d!="object"||Array.isArray(d)){r.push(`Destination "${a}": muss ein Objekt sein.`);continue}let l=Tn(d,e);if(l.missing.length>0){r.push(`Destination "${a}": Umgebungsvariable(n) nicht gesetzt: ${l.missing.join(", ")}.`);continue}let c={...l.value,name:a},u=Ee.safeParse(c);if(!u.success){r.push(...Cr(`Destination "${a}": `,u.error.issues));continue}s.set(a,u.data);}let o=n.data.tokens;return o.forEach((a,d)=>{Object.hasOwn(n.data.destinations,a.destination)||r.push(`tokens[${d}]${a.label?` ("${a.label}")`:""}: Destination "${a.destination}" existiert nicht.`);}),r.length>0?{ok:false,errors:r}:{ok:true,value:{destinations:s,tokens:o}}}function Ge(t,e){let n=createHash("sha256").update(t).digest(),r=createHash("sha256").update(e).digest();return timingSafeEqual(n,r)}var oa="gsm",Pr=10,vt=6e4,aa=1e4;function kr(t){return `sha256:${createHash("sha256").update(t,"utf8").digest("hex")}`}function _r(t){let e=randomBytes(32).toString("base64url"),n=`${oa}_${t}_${e}`;return {token:n,hash:kr(n)}}var Ce=class{apiKey;tokens;now;failures=new Map;constructor(e){this.apiKey=e.apiKey,this.tokens=e.tokens,this.now=e.now??Date.now;}enforced(){return this.apiKey!==void 0||this.tokens().length>0}authenticate(e,n){if(!this.enforced())return {kind:"open"};let r=this.blockedFor(n);if(r!==void 0)return {kind:"rate-limited",retryAfterSeconds:r};let s=this.check(e);return s.kind==="rejected"?this.recordFailure(n):this.failures.delete(n),s}check(e){let n=ca(e),r=n??"",s=this.apiKey!==void 0&&Ge(r,this.apiKey),i=Buffer.from(kr(r),"utf8"),o,a=false;for(let d of this.tokens()){let l=Buffer.from(d.hash,"utf8");l.length===i.length&&timingSafeEqual(l,i)&&(d.revoked?a=true:o||(o=d));}return o?{kind:"token",token:o}:s?{kind:"api-key"}:n===void 0?{kind:"rejected",reason:"Kein Bearer-Token im Authorization-Header."}:a?{kind:"rejected",reason:"Das Token wurde widerrufen."}:{kind:"rejected",reason:"Unbekanntes Token oder falscher API-Key."}}blockedFor(e){let n=this.failures.get(e);if(!n)return;let r=this.now()-n.windowStart;if(r>vt){this.failures.delete(e);return}if(n.count>=Pr)return Math.max(1,Math.ceil((vt-r)/1e3))}recordFailure(e){let n=this.now();if(this.failures.size>=aa)for(let[s,i]of this.failures)n-i.windowStart>vt&&this.failures.delete(s);let r=this.failures.get(e);if(!r||n-r.windowStart>vt){this.failures.set(e,{count:1,windowStart:n});return}r.count+=1,r.count===Pr&&(r.windowStart=n);}};function ca(t){return typeof t!="string"?void 0:/^Bearer\s+(.+)$/i.exec(t.trim())?.[1]?.trim()||void 0}var Or=["core","odata","idoc","rfc"],P=class extends Error{constructor(n,r=400){super(n);this.status=r;this.name="AdminError";}},Re=class{filePath;env;now;constructor(e){this.filePath=e.filePath,this.env=e.env,this.now=e.now??(()=>new Date);}listDestinations(){let e=this.read({mustExist:false});return Object.keys(e.destinations).length===0&&e.tokens.length===0?[]:(this.validate(e,true,422),Object.entries(e.destinations).map(([n,r])=>{let s=Ir(e,n);return {name:n,baseUrl:String(r.baseUrl??""),authType:String(r.authType??""),sapClient:r.sapClient===void 0?void 0:String(r.sapClient),tokens:s.length,activeTokens:s.filter(i=>i.revoked!==true).length}}))}addDestination(e,n,r={}){if(!vn.test(e))throw new P(`"${e}" is not a valid destination name (letters, digits, "_", ".", "-", max 64 characters).`,400);let s=this.read({mustExist:false}),i=s.destinations[e]!==void 0;if(i&&!r.replace)throw new P(`Destination "${e}" already exists. Use --replace to overwrite it.`,409);let o;if(r.fromEnv){let l;try{let{name:c,...u}=We(this.env);l=u;}catch(c){throw new P(c instanceof Error?c.message:String(c),400)}o=Nr(l);}else if(o=Nr(n),Object.keys(o).length===0)throw new P("Nothing to add: pass --from-env or --base-url/--auth-type and the auth fields.",400);let a={...s,destinations:{...s.destinations,[e]:o}},{warnings:d}=this.validate(a,!!r.allowMissingEnv,400);return this.write(a),{replaced:i,warnings:d}}removeDestination(e,n={}){let r=this.read({mustExist:true});if(!r.destinations[e])throw new P(`Destination "${e}" not found in ${this.filePath}.`,404);let s=Ir(r,e);if(s.length>0&&!n.force)throw new P(`Destination "${e}" still has ${s.length} token(s) (${s.map(d=>typeof d.label=="string"?d.label:pe(d.hash)).join(", ")}). Revoke them first, or pass --force to remove them together.`,409);if(Object.keys(r.destinations).length===1)throw new P(`Destination "${e}" is the last one in ${this.filePath}; the server needs at least one. Add a replacement first, or delete the file.`,409);let{[e]:i,...o}=r.destinations,a={...r,destinations:o,tokens:r.tokens.filter(d=>d.destination!==e)};return this.validate(a,true,400),this.write(a),{removedTokens:s.length}}listTokens(){let e=this.read({mustExist:false});return Object.keys(e.destinations).length===0&&e.tokens.length===0?[]:(this.validate(e,true,422),e.tokens.map(n=>({label:typeof n.label=="string"?n.label:void 0,destination:String(n.destination),hash:pe(n.hash),policy:n.policy,revoked:n.revoked===true,createdAt:typeof n.createdAt=="string"?n.createdAt:void 0})))}issueToken(e,n={}){let r=this.read({mustExist:true});if(!r.destinations[e])throw new P(`Destination "${e}" not found in ${this.filePath}. Known: ${Object.keys(r.destinations).join(", ")||"(none)"}.`,404);let s={};if(n.readOnly&&(s.readOnly=true),n.tiers!==void 0){let l=n.tiers.map(u=>u.trim()).filter(u=>u.length>0),c=l.filter(u=>!Or.includes(u));if(c.length>0||l.length===0)throw new P(`Unknown tier(s): ${c.join(", ")||"(empty)"}. Allowed: ${Or.join(", ")}.`,400);s.tiers=l;}if(n.label!==void 0&&n.label.length>200)throw new P("Label must not exceed 200 characters.",400);let{token:i,hash:o}=_r(e),a={hash:o,destination:e,...n.label?{label:n.label}:{},...Object.keys(s).length>0?{policy:s}:{},createdAt:this.now().toISOString()},d={...r,tokens:[...r.tokens,a]};return this.validate(d,true,400),this.write(d),{token:i,hash:o,entry:a}}revokeToken(e){let n=this.read({mustExist:true}),r=n.tokens.filter(c=>c.label===e),s=e.startsWith("sha256:")?e.slice(7):e,i=/^[0-9a-f]{8,64}$/i.test(s)?n.tokens.filter(c=>typeof c.hash=="string"&&c.hash.slice(7).startsWith(s.toLowerCase())):[],o=r.length>0?r:i;if(o.length===0)throw new P(`No token matches "${e}". Use the label or at least 8 hex characters of the hash (see: tokens list).`,404);if(o.length>1)throw new P(`"${e}" is ambiguous (${o.length} tokens). Use a longer hash prefix: ${o.map(c=>pe(c.hash)).join(", ")}.`,409);let a=o[0],d=typeof a.label=="string"?a.label:void 0;if(a.revoked===true)return {hash:pe(a.hash),label:d,alreadyRevoked:true};let l={...n,tokens:n.tokens.map(c=>c===a?{...c,revoked:true}:c)};return this.validate(l,true,400),this.write(l),{hash:pe(a.hash),label:d,alreadyRevoked:false}}read({mustExist:e}){if(!existsSync(this.filePath)){if(e)throw new P(`${this.filePath}: file not found.`,404);return {version:1,destinations:{},tokens:[]}}let n;try{n=JSON.parse(readFileSync(this.filePath,"utf8"));}catch(s){throw new P(`${this.filePath}: not valid JSON (${s instanceof Error?s.message:String(s)}).`,422)}if(n===null||typeof n!="object"||Array.isArray(n))throw new P(`${this.filePath}: must be a JSON object with "version" and "destinations".`,422);let r=n;return {version:1,destinations:r.destinations??{},tokens:r.tokens??[],...r.version!==void 0&&r.version!==1?{version:r.version}:{}}}validate(e,n,r){let s=Tn(e.destinations,this.env),i=[],o=this.env;if(s.missing.length>0&&n){o={...this.env};for(let d of s.missing)o[d]="placeholder";i.push(`Environment variable(s) not set here: ${s.missing.join(", ")}. The server needs them at load time.`);}let a=wt(e,o);if(!a.ok)throw new P(`${r===400?"Refusing to write an invalid file":`${this.filePath} is invalid`}:
|
|
63
63
|
${a.errors.map(d=>` - ${d}`).join(`
|
|
64
64
|
`)}`,r);return {warnings:i}}write(e){let n=dirname(this.filePath);mkdirSync(n,{recursive:true});let r=`${this.filePath}.${process.pid}.tmp`,s=`${JSON.stringify(e,null,2)}
|
|
65
|
-
`;try{writeFileSync(r,s,{mode:384}),renameSync(r,this.filePath);}catch(i){try{unlinkSync(r);}catch{}throw i}}};function
|
|
65
|
+
`;try{writeFileSync(r,s,{mode:384}),renameSync(r,this.filePath);}catch(i){try{unlinkSync(r);}catch{}throw i}}};function Ir(t,e){return t.tokens.filter(n=>n.destination===e)}function pe(t){return typeof t=="string"?t.replace(/^sha256:/,"").slice(0,12):"?"}function Nr(t){return Object.fromEntries(Object.entries(t).filter(([,e])=>e!==void 0))}var Dr=new Set(["destinations","tokens"]),Tt=`Usage:
|
|
66
66
|
guniweb-sap-mcp destinations list
|
|
67
67
|
guniweb-sap-mcp destinations add <name> (--from-env | --base-url <url> --auth-type <type> [fields])
|
|
68
68
|
[--sap-client <c>] [--set <field>=<value>]... [--replace]
|
|
@@ -87,26 +87,26 @@ Auth types: basic, user-basic (personal SAP login per request \u2014 the caller
|
|
|
87
87
|
|
|
88
88
|
"tokens issue" prints the plain token ONCE on stdout; only its SHA-256 hash is stored.
|
|
89
89
|
Put the token into an n8n Bearer Auth credential on the MCP Client node.
|
|
90
|
-
`,V=class extends Error{constructor(n,r=1){super(n);this.exitCode=r;}};async function
|
|
90
|
+
`,V=class extends Error{constructor(n,r=1){super(n);this.exitCode=r;}};async function xr(t,e){let n=ba(t);try{if(n.flags.help)return e.stdout(Tt),0;let[r,s,...i]=n.positional;if(r==="destinations")switch(s){case "list":return wa(n,e);case "add":return Ta(i[0],n,e);case "remove":return Aa(i[0],n,e);default:throw new V(`Unknown command "destinations ${s??""}".
|
|
91
91
|
|
|
92
|
-
${
|
|
92
|
+
${Tt}`,2)}if(r==="tokens")switch(s){case "list":return Ea(n,e);case "issue":return Ca(i[0],n,e);case "revoke":return Ra(i[0],n,e);default:throw new V(`Unknown command "tokens ${s??""}".
|
|
93
93
|
|
|
94
|
-
${
|
|
94
|
+
${Tt}`,2)}throw new V(Tt,2)}catch(r){return r instanceof V?(e.stderr(`${r.message}
|
|
95
95
|
`),r.exitCode):r instanceof P?(e.stderr(`${r.message}
|
|
96
96
|
`),1):(e.stderr(`Error: ${r instanceof Error?r.message:String(r)}
|
|
97
|
-
`),1)}}var
|
|
98
|
-
`),0}var
|
|
97
|
+
`),1)}}var ya=new Set(["help","h","from-env","replace","force","read-only","allow-missing-env"]),Sa=new Set(["set"]);function ba(t){let e=[],n={};for(let r=0;r<t.length;r++){let s=t[r];if(s==="-h"){n.help=true;continue}if(!s.startsWith("--")){e.push(s);continue}let i=s.indexOf("="),o=i===-1?s.slice(2):s.slice(2,i);if(ya.has(o)){n[o]=true;continue}let a=i===-1?t[++r]:s.slice(i+1);if(a===void 0)throw new V(`Missing value for --${o}.`,2);if(Sa.has(o)){let d=n[o]??[];d.push(a),n[o]=d;}else n[o]=a;}return {positional:e,flags:n}}function At(t,e){let n=t.flags[e];return typeof n=="string"?n:void 0}function Pe(t,e){let n=At(t,"destinations")??e.env.SAP_MCP_DESTINATIONS;if(!n||n.trim().length===0)throw new V("No destinations file given. Pass --destinations <path> or set SAP_MCP_DESTINATIONS.",2);return new Re({filePath:resolve(n.trim()),env:e.env,now:e.now})}function wa(t,e){let n=Pe(t,e).listDestinations();return e.stdout(`${Mr(["NAME","BASE URL","AUTH","CLIENT","TOKENS"],n.map(r=>[r.name,r.baseUrl,r.authType,r.sapClient??"-",String(r.tokens)]))}
|
|
98
|
+
`),0}var va={"base-url":"baseUrl","auth-type":"authType","sap-client":"sapClient",username:"username",password:"password","api-key":"apiKey","client-id":"clientId","client-secret":"clientSecret","token-service-url":"tokenServiceUrl"};function Ta(t,e,n){if(!t)throw new V("Usage: destinations add <name> ...",2);let r=Pe(e,n),s={};if(!e.flags["from-env"]){for(let[a,d]of Object.entries(va)){let l=At(e,a);l!==void 0&&(s[d]=l);}for(let a of e.flags.set??[]){let d=a.indexOf("=");if(d<=0)throw new V(`--set expects field=value, got "${a}".`,2);s[a.slice(0,d)]=a.slice(d+1);}}let{replaced:i,warnings:o}=r.addDestination(t,s,{replace:!!e.flags.replace,allowMissingEnv:!!e.flags["allow-missing-env"],fromEnv:!!e.flags["from-env"]});for(let a of o)n.stderr(`Warning: ${a}
|
|
99
99
|
`);return n.stderr(`Destination "${t}" ${i?"replaced":"added"} in ${r.filePath}.
|
|
100
|
-
`),0}function
|
|
101
|
-
`),0}function
|
|
102
|
-
`),0}function
|
|
100
|
+
`),0}function Aa(t,e,n){if(!t)throw new V("Usage: destinations remove <name> [--force]",2);let{removedTokens:r}=Pe(e,n).removeDestination(t,{force:!!e.flags.force});return n.stderr(`Destination "${t}" removed${r>0?` together with ${r} token(s)`:""}.
|
|
101
|
+
`),0}function Ea(t,e){let n=Pe(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(`${Mr(["LABEL","DESTINATION","HASH","POLICY","STATUS","CREATED"],n)}
|
|
102
|
+
`),0}function Ca(t,e,n){if(!t)throw new V("Usage: tokens issue <destination> [--label ...]",2);let r=Pe(e,n),s=At(e,"tiers"),i=At(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 ${pe(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 Ra(t,e,n){if(!t)throw new V("Usage: tokens revoke <label | hash-prefix>",2);let{hash:r,label:s,alreadyRevoked:i}=Pe(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
|
-
`),0)}function
|
|
107
|
-
`)}var me="default",
|
|
106
|
+
`),0)}function Mr(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
|
+
`)}var me="default",Da=250,Ur=1e3,ke=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={...We(e),name:me};return new t({source:"env",config:{destinations:new Map([[me,n]]),tokens:[]},env:e})}static async fromFile(e,n={}){let r=n.env??process.env,s=resolve(e),i=r.SAP_BASE_URL?{...We(r),name:me}: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([[me,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(d=>` - ${d}`).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(me);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:Mr},(e,n)=>{(e.mtimeMs!==n.mtimeMs||e.size!==n.size)&&this.scheduleReload();}),this.startupRecheck=setTimeout(()=>{this.startupRecheck=void 0,this.scheduleReload();},Mr+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();},_a),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=wt(n,this.env);return r.ok?(this.envDefault&&(r.value.destinations.has(me)?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(me,this.envDefault)),{ok:true,value:r.value,raw:e}):r}};function Ur(t){return createHash("sha256").update(JSON.stringify(t)??"undefined").digest("hex")}function An(t){return t.slice(7,19)}function $r(t,e){let n=new Map;for(let s of t)n.set(s.hash,{label:s.label??An(s.hash),destination:s.destination,revoked:s.revoked===true,policy:Ur(s.policy??null)});let r=new Map;for(let s of e){let i=new Map;for(let[o,a]of Object.entries(s))o!=="name"&&i.set(o,Ur(a));r.set(s.name??"default",i);}return {tokens:n,destinations:r}}function Lr(t,e){let n=[];for(let[r,s]of e.destinations){let i=t.destinations.get(r);if(!i){n.push({type:"destination-added",name:r});continue}let o=[...new Set([...s.keys(),...i.keys()])].filter(a=>s.get(a)!==i.get(a)).sort();o.length>0&&n.push({type:"destination-changed",name:r,fields:o});}for(let r of t.destinations.keys())e.destinations.has(r)||n.push({type:"destination-removed",name:r});for(let[r,s]of e.tokens){let i=t.tokens.get(r),o={tokenLabel:s.label,hashPrefix:An(r),destination:s.destination};if(!i){n.push({type:s.revoked?"token-added-revoked":"token-added",...o});continue}!i.revoked&&s.revoked?n.push({type:"token-revoked",...o}):i.revoked&&!s.revoked&&n.push({type:"token-reactivated",...o});let a=[...i.label!==s.label?["label"]:[],...i.destination!==s.destination?["destination"]:[],...i.policy!==s.policy?["policy"]:[]];a.length>0&&n.push({type:"token-changed",...o,fields:a});}for(let[r,s]of t.tokens)e.tokens.has(r)||n.push({type:"token-removed",tokenLabel:s.label,hashPrefix:An(r),destination:s.destination});return n}var Br="@guniweb/sap-mcp-governance";function xa(t){return isAbsolute(t)?t:createRequire(import.meta.url).resolve(t)}function Ma(t){let e=t?.code;return e==="MODULE_NOT_FOUND"||e==="ERR_MODULE_NOT_FOUND"}async function Fr(t,e={},n){let r=e.resolve??xa,s=e.importer??(u=>import(u)),i=n?.trim()||t.env.SAP_MCP_GOVERNANCE_MODULE?.trim()||void 0,o=i??Br,a;try{a=r(o);}catch(u){if(!i&&Ma(u))return {status:"not-installed"};throw new Error(`Governance-Modul "${o}" ist konfiguriert, aber nicht auffindbar: ${u.message}`)}let d;try{d=await s(pathToFileURL(a).href);}catch(u){throw new Error(`Governance-Modul "${o}" konnte nicht geladen werden: ${u.message}`)}let l=d.createGovernance??d.default;if(typeof l!="function")throw new Error(`Governance-Modul "${o}" exportiert keine Funktion createGovernance (oder default).`);let c=await l(t);if(c?.apiVersion!==1)throw new Error(`Governance-Modul "${o}" spricht apiVersion ${String(c?.apiVersion)}, dieser Server ${1}. Passende Versionen von Server und Modul verwenden.`);if(typeof c.onToolCall!="function")throw new Error(`Governance-Modul "${o}" hat keine Funktion onToolCall.`);return {status:"active",module:c,specifier:o}}var Ua=new Set(["off","false","0","no"]),Hr="https://guniweb.de/sap-mcp",jr="support@guniweb.de";function Vr(t){let e=t.SAP_MCP_GOVERNANCE_NOTICE?.trim().toLowerCase();if(!(e&&Ua.has(e)))return {msg:`Governance module not loaded \u2014 SAP access is logged for operations only. The GuniWeb Governance module adds a durable, queryable call journal for audits (who, which object, what result; no payload). More: ${Hr} \xB7 ${jr} (hide with SAP_MCP_GOVERNANCE_NOTICE=off)`,fields:{governance:"not-installed",learnMore:Hr,contact:jr}}}var _e=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 La=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",parseTagValue:false,processEntities:false,htmlEntities:false,isArray:t=>t==="IDOC"||t.startsWith("E1")||t.startsWith("E2")}),qe=new Set(["@_SEGMENT","@_BEGIN"]);function En(t,e){return !!(t.startsWith("E1")||t.startsWith("E2")||typeof e=="object"&&e!==null&&"@_SEGMENT"in e)}function Wr(t){let e=[];for(let[n,r]of Object.entries(t)){if(qe.has(n)||!En(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[d,l]of Object.entries(i))if(!qe.has(d))if(En(d,l)){let c=Array.isArray(l)?l:[l];for(let u of c){if(typeof u!="object"||u===null)continue;let p={},m=Wr(u);for(let[f,g]of Object.entries(u))qe.has(f)||!En(f,g)&&typeof g!="object"&&(p[f]=String(g));a.push({name:d,fields:p,...m.length>0?{children:m}:{}});}}else typeof l!="object"&&(o[d]=String(l));e.push({name:n,fields:o,...a.length>0?{children:a}:{}});}}return e}function Gr(t){let e=La.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,d={};if(a){let u=Array.isArray(a)?a[0]:a;for(let[p,m]of Object.entries(u))!qe.has(p)&&typeof m!="object"&&(d[p]=String(m));}let l={};for(let[u,p]of Object.entries(o))u==="EDI_DC40"||qe.has(u)||(l[u]=p);let c=Wr(l);return {idocType:r,controlRecord:d,segments:c}}var Ba=10*1024*1024,Et=class extends Error{};function Fa(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 Et(`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 Ha(t,e){if(e.length===0||typeof t!="string")return false;if(t.startsWith("Bearer "))return Ge(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:Ge(n.slice(r+1),e)}return false}function ja(t){if(typeof t!="string")return true;let e=(t.split(";")[0]??"").trim().toLowerCase();return e.endsWith("xml")||e==="application/x-sap.idoc"}function Ct(t,e={}){let n=e.maxBodyBytes??Ba;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&&!Ha(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(!ja(r.headers["content-type"])){s.writeHead(415,{"Content-Type":"text/plain"}),s.end("Unsupported Media Type: expected XML"),r.destroy();return}Fa(r,n).then(i=>{let{idocType:o,controlRecord:a,segments:d}=Gr(i),l={docnum:a.DOCNUM||`UNKNOWN_${Date.now()}`,idocType:o,mesType:a.MESTYP||"",receivedAt:new Date,rawXml:i,parsed:{controlRecord:a,segments:d}};t.add(l),s.writeHead(200,{"Content-Type":"text/plain"}),s.end("OK");}).catch(i=>{if(i instanceof Et){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 qr(t,e,n={}){let r=Ct(e,n);return new Promise((s,i)=>{let o=createServer((d,l)=>{d.method==="POST"&&d.url==="/idoc"?r(d,l):(l.writeHead(404,{"Content-Type":"text/plain"}),l.end("Not Found"));}),a=()=>{let d=o.address(),l=typeof d=="object"&&d!==null?d.port:t;s({port:l,close:()=>new Promise((c,u)=>{o.close(p=>p?u(p):c());})});};n.host?o.listen(t,n.host,a):o.listen(t,a),o.on("error",i);})}var zr=1;function Rt(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!==zr||!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 Pt(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:zr,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 Xa=30,Ja="catalog-cache.json",Yr="catalog",re=class{cache=null;ttlMs;key;persistence;constructor(e={}){let n=typeof e=="number"?{ttlMinutes:e}:e;this.ttlMs=(n.ttlMinutes??Xa)*60*1e3;let r=(n.baseUrl??"").replace(/\/+$/,"").toLowerCase();if(this.key=r?`${r}|${Yr}`:Yr,n.persistDir){this.persistence={directory:n.persistDir,fileName:Ja,ttlMs:this.ttlMs,...n.onWarn?{onWarn:n.onWarn}:{}};let s=Rt(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&&Pt(this.cache?[{key:this.key,value:this.cache.entries,storedAt:this.cache.timestamp}]:[],this.persistence);}};var Cn=0;function Xr(t){let e=t.docnum??`${Date.now().toString().padStart(13,"0")}${String(Cn++).padStart(3,"0")}`;Cn>=1e3&&(Cn=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 Za(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 Qa(t,e){return t.filter(n=>n.name===e).length}function Jr(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=Qa(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]??"",d=Za(s.name,o,a);d&&n.push(d);}s.children&&s.children.length>0&&Jr(s.children,e,n);}}function Zr(t,e){if(!e)return [];let n=new Map;for(let s of e.segments)n.set(s.name,s);let r=[];return Jr(t,n,r),r}var tc=new XMLBuilder({ignoreAttributes:false,attributeNamePrefix:"@_",format:true,suppressEmptyNode:false});function Qr(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=Qr(n),s=e[n.name];s?Array.isArray(s)?s.push(r):e[n.name]=[s,r]:e[n.name]=r;}return e}function es(t,e,n){let r={};for(let i of n){let o=Qr(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 tc.build(s)}var kt=class{constructor(e,n,r){this.httpClient=e;this.sapClient=n;this.partnerDefaults=r;}async send(e){let n={...this.partnerDefaults,...e.partnerOverrides},r=Xr({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=Zr(e.segments,e.metadata);if(u.length>0)return {success:false,docnum:r.DOCNUM,message:"Validation failed",violations:u}}let s=es(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 c=a.match(/<title>(.*?)<\/title>/)?.[1]??"IDoc send failed";return {success:false,docnum:r.DOCNUM,message:c}}};var _t={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","sap_media_upload","sap_media_download"],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"]},Ke=new Set(["sap_create","sap_update","sap_delete","sap_batch","sap_media_upload","sap_idoc_send","sap_rfc_call","sap_bapi_call","sap_bapi_commit"]),Rn=["core","odata"],ts=new Map;for(let[t,e]of Object.entries(_t))for(let n of e)ts.set(n,t);var Ot=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=_t[e],r=[];for(let s of n){if(this.options.readOnly&&Ke.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 _t[e]}isReadOnlyMode(){return this.options.readOnly}shouldToolBeVisible(e){let n=ts.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&&Ke.has(e)):true}};function ns(t){let e=t.tiers??Rn,n=[];for(let r of e)if(!(r==="idoc"&&!t.hasIdocConfig)&&!(r==="rfc"&&!t.hasRfcConfig))for(let s of _t[r])Ke.has(s)&&n.push(s);return n}var rs="0.0.0-unknown",ss="guniweb-sap-mcp";function oc(t){if(typeof t!="string"||t.length===0)return rs;let e=resolve(t);for(;;){try{let r=readFileSync(join(e,"package.json"),"utf-8"),s=JSON.parse(r);if(s.name===ss&&typeof s.version=="string")return s.version}catch{}let n=dirname(e);if(n===e)return rs;e=n;}}var X=oc(import.meta.dirname),is=ss;var ac=/^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})(-.*)?$/,os=/^0+$/;function Pn(t){if(typeof t!="string")return;let e=t.trim();if(e.length>512)return;let n=ac.exec(e);if(!n)return;let[,r,s,i,,o]=n;if(r!=="ff"&&!(r==="00"&&o!==void 0)&&!(os.test(s)||os.test(i)))return {traceId:s,parentId:i}}function as(t){if(!(!t||typeof t!="object")){for(let[e,n]of Object.entries(t))if(e.toLowerCase()==="traceparent")return Array.isArray(n)?void 0:Pn(n)}}var It=30,cc={authKind:"stdio",personal:false};function dc(t,e){return Ke.has(t)||(e.mutatingCalls?.count??0)>0?"write":t==="sap_function"&&e.sapCalls.count===0?"unknown":"read"}var lc=5e3;function uc(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 d=Date.now()-n;i({method:"notifications/progress",params:{progressToken:s,progress:Math.round(d/1e3),...r.budgetMs?{total:Math.round(r.budgetMs/1e3)}:{},message:`${e.toolName}: ${Math.round(d/1e3)} s, ${e.sapCalls.count} SAP-Aufruf(e)`}}).catch(()=>{});},r.afterMs);return a.unref?.(),()=>clearInterval(a)}var pc="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 mc(t){return {...t,content:[...t.content,{type:"text",text:`\u26A0\uFE0F ${pc}`}]}}function fc(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 gc(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 cs(t,e,n={}){let{toolTimeoutMs:r,logger:s,tlsVerifyDisabled:i,governance:o}=n,a=n.progressAfterMs??lc;return async(d,l)=>{let c=Zn(),{signal:u,deadline:p}=nr(l?.signal,r),m=as(l?.requestInfo?.headers)??Pn(l?._meta?.traceparent),f={correlationId:c,...m?{traceId:m.traceId}:{},toolName:t,signal:u,deadline:p,sapCalls:{count:0},mutatingCalls:{count:0},created:{}},g=Date.now();return Qn(f,async()=>{let h=uc(l,f,g,{afterMs:a,...r?{budgetMs:r}:{}}),b="ok",y,E=(async()=>e(d,l))();E.catch(()=>{});try{y=await Promise.race([E,hc(u,t)]),y?.isError&&(b="error");}catch(v){if(v instanceof F)b=v.timedOut?"timeout":"aborted",y=v.timedOut?fc(t,c,Date.now()-g,f.sapCalls.count,r??0):gc(t,c);else {b="error";let x=v instanceof Error?v.message:String(v);y={content:[{type:"text",text:JSON.stringify({error:x,tool:t,correlationId:c})}],isError:true};}}b==="ok"&&u?.aborted&&(b=u.reason?.name==="TimeoutError"?"timeout":"aborted"),h();let S=i?mc(y):y;if(s?.info({correlationId:c,...m?{traceId:m.traceId,parentId:m.parentId}:{},tool:t,outcome:b,durationMs:Date.now()-g,sapCalls:f.sapCalls.count,resultBytes:yc(y),...r?{budgetMs:r}:{}},"MCP tool call"),o){let v={apiVersion:1,serverVersion:n.serverVersion??X,correlationId:c,...m?{traceId:m.traceId,parentId:m.parentId}:{},tool:t,kind:dc(t,f),outcome:b,startedAt:new Date(g).toISOString(),durationMs:Date.now()-g,sapCalls:f.sapCalls.count,...n.destination?{destination:n.destination}:{},...b==="ok"&&f.created?.entity?{createdEntity:f.created.entity}:{},identity:n.callerIdentity??cc,args:d,result:y};try{o.onToolCall(v);}catch(x){s?.error({err:x,correlationId:c,tool:t,module:o.name},"Governance-Modul: Aufruf konnte nicht gemeldet werden");}}return S})}}function hc(t,e){return t?new Promise((n,r)=>{let s=()=>{let i=t.reason?.name==="TimeoutError";r(new F(i,e));};if(t.aborted){s();return}t.addEventListener("abort",s,{once:true});}):new Promise(()=>{})}function yc(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 Sc=1440*60*1e3,bc="metadata-cache.json",I=class{cache=new Map;ttlMs;baseUrl;persistence;constructor(e={}){if(this.ttlMs=e.ttlMs??Sc,this.baseUrl=wc(e.baseUrl),e.persistDir){this.persistence={directory:e.persistDir,fileName:bc,ttlMs:this.ttlMs,...e.onWarn?{onWarn:e.onWarn}:{}};for(let n of Rt(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&&Pt([...this.cache.entries()].map(([e,n])=>({key:e,value:n.value,storedAt:n.storedAt})),this.persistence);}};function wc(t){return t?t.replace(/\/+$/,"").toLowerCase():""}function ds(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(me);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:Ur},(e,n)=>{(e.mtimeMs!==n.mtimeMs||e.size!==n.size)&&this.scheduleReload();}),this.startupRecheck=setTimeout(()=>{this.startupRecheck=void 0,this.scheduleReload();},Ur+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();},Da),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=wt(n,this.env);return r.ok?(this.envDefault&&(r.value.destinations.has(me)?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(me,this.envDefault)),{ok:true,value:r.value,raw:e}):r}};function $r(t){return createHash("sha256").update(JSON.stringify(t)??"undefined").digest("hex")}function An(t){return t.slice(7,19)}function Lr(t,e){let n=new Map;for(let s of t)n.set(s.hash,{label:s.label??An(s.hash),destination:s.destination,revoked:s.revoked===true,policy:$r(s.policy??null)});let r=new Map;for(let s of e){let i=new Map;for(let[o,a]of Object.entries(s))o!=="name"&&i.set(o,$r(a));r.set(s.name??"default",i);}return {tokens:n,destinations:r}}function Br(t,e){let n=[];for(let[r,s]of e.destinations){let i=t.destinations.get(r);if(!i){n.push({type:"destination-added",name:r});continue}let o=[...new Set([...s.keys(),...i.keys()])].filter(a=>s.get(a)!==i.get(a)).sort();o.length>0&&n.push({type:"destination-changed",name:r,fields:o});}for(let r of t.destinations.keys())e.destinations.has(r)||n.push({type:"destination-removed",name:r});for(let[r,s]of e.tokens){let i=t.tokens.get(r),o={tokenLabel:s.label,hashPrefix:An(r),destination:s.destination};if(!i){n.push({type:s.revoked?"token-added-revoked":"token-added",...o});continue}!i.revoked&&s.revoked?n.push({type:"token-revoked",...o}):i.revoked&&!s.revoked&&n.push({type:"token-reactivated",...o});let a=[...i.label!==s.label?["label"]:[],...i.destination!==s.destination?["destination"]:[],...i.policy!==s.policy?["policy"]:[]];a.length>0&&n.push({type:"token-changed",...o,fields:a});}for(let[r,s]of t.tokens)e.tokens.has(r)||n.push({type:"token-removed",tokenLabel:s.label,hashPrefix:An(r),destination:s.destination});return n}var Fr="@guniweb/sap-mcp-governance";function La(t){return isAbsolute(t)?t:createRequire(import.meta.url).resolve(t)}function Ba(t){let e=t?.code;return e==="MODULE_NOT_FOUND"||e==="ERR_MODULE_NOT_FOUND"}async function Hr(t,e={},n){let r=e.resolve??La,s=e.importer??(u=>import(u)),i=n?.trim()||t.env.SAP_MCP_GOVERNANCE_MODULE?.trim()||void 0,o=i??Fr,a;try{a=r(o);}catch(u){if(!i&&Ba(u))return {status:"not-installed"};throw new Error(`Governance-Modul "${o}" ist konfiguriert, aber nicht auffindbar: ${u.message}`)}let d;try{d=await s(pathToFileURL(a).href);}catch(u){throw new Error(`Governance-Modul "${o}" konnte nicht geladen werden: ${u.message}`)}let l=d.createGovernance??d.default;if(typeof l!="function")throw new Error(`Governance-Modul "${o}" exportiert keine Funktion createGovernance (oder default).`);let c=await l(t);if(c?.apiVersion!==1)throw new Error(`Governance-Modul "${o}" spricht apiVersion ${String(c?.apiVersion)}, dieser Server ${1}. Passende Versionen von Server und Modul verwenden.`);if(typeof c.onToolCall!="function")throw new Error(`Governance-Modul "${o}" hat keine Funktion onToolCall.`);return {status:"active",module:c,specifier:o}}var Fa=new Set(["off","false","0","no"]),jr="https://guniweb.de/sap-mcp",Vr="support@guniweb.de";function Wr(t){let e=t.SAP_MCP_GOVERNANCE_NOTICE?.trim().toLowerCase();if(!(e&&Fa.has(e)))return {msg:`Governance module not loaded \u2014 SAP access is logged for operations only. The GuniWeb Governance module adds a durable, queryable call journal for audits (who, which object, what result; no payload). More: ${jr} \xB7 ${Vr} (hide with SAP_MCP_GOVERNANCE_NOTICE=off)`,fields:{governance:"not-installed",learnMore:jr,contact:Vr}}}var _e=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 ja=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",parseTagValue:false,processEntities:false,htmlEntities:false,isArray:t=>t==="IDOC"||t.startsWith("E1")||t.startsWith("E2")}),qe=new Set(["@_SEGMENT","@_BEGIN"]);function En(t,e){return !!(t.startsWith("E1")||t.startsWith("E2")||typeof e=="object"&&e!==null&&"@_SEGMENT"in e)}function Gr(t){let e=[];for(let[n,r]of Object.entries(t)){if(qe.has(n)||!En(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[d,l]of Object.entries(i))if(!qe.has(d))if(En(d,l)){let c=Array.isArray(l)?l:[l];for(let u of c){if(typeof u!="object"||u===null)continue;let p={},m=Gr(u);for(let[f,g]of Object.entries(u))qe.has(f)||!En(f,g)&&typeof g!="object"&&(p[f]=String(g));a.push({name:d,fields:p,...m.length>0?{children:m}:{}});}}else typeof l!="object"&&(o[d]=String(l));e.push({name:n,fields:o,...a.length>0?{children:a}:{}});}}return e}function qr(t){let e=ja.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,d={};if(a){let u=Array.isArray(a)?a[0]:a;for(let[p,m]of Object.entries(u))!qe.has(p)&&typeof m!="object"&&(d[p]=String(m));}let l={};for(let[u,p]of Object.entries(o))u==="EDI_DC40"||qe.has(u)||(l[u]=p);let c=Gr(l);return {idocType:r,controlRecord:d,segments:c}}var Va=10*1024*1024,Et=class extends Error{};function Wa(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 Et(`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 Ga(t,e){if(e.length===0||typeof t!="string")return false;if(t.startsWith("Bearer "))return Ge(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:Ge(n.slice(r+1),e)}return false}function qa(t){if(typeof t!="string")return true;let e=(t.split(";")[0]??"").trim().toLowerCase();return e.endsWith("xml")||e==="application/x-sap.idoc"}function Ct(t,e={}){let n=e.maxBodyBytes??Va;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&&!Ga(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(!qa(r.headers["content-type"])){s.writeHead(415,{"Content-Type":"text/plain"}),s.end("Unsupported Media Type: expected XML"),r.destroy();return}Wa(r,n).then(i=>{let{idocType:o,controlRecord:a,segments:d}=qr(i),l={docnum:a.DOCNUM||`UNKNOWN_${Date.now()}`,idocType:o,mesType:a.MESTYP||"",receivedAt:new Date,rawXml:i,parsed:{controlRecord:a,segments:d}};t.add(l),s.writeHead(200,{"Content-Type":"text/plain"}),s.end("OK");}).catch(i=>{if(i instanceof Et){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 Kr(t,e,n={}){let r=Ct(e,n);return new Promise((s,i)=>{let o=createServer((d,l)=>{d.method==="POST"&&d.url==="/idoc"?r(d,l):(l.writeHead(404,{"Content-Type":"text/plain"}),l.end("Not Found"));}),a=()=>{let d=o.address(),l=typeof d=="object"&&d!==null?d.port:t;s({port:l,close:()=>new Promise((c,u)=>{o.close(p=>p?u(p):c());})});};n.host?o.listen(t,n.host,a):o.listen(t,a),o.on("error",i);})}var Yr=1;function Rt(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!==Yr||!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 Pt(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:Yr,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 ec=30,tc="catalog-cache.json",Xr="catalog",re=class{cache=null;ttlMs;key;persistence;constructor(e={}){let n=typeof e=="number"?{ttlMinutes:e}:e;this.ttlMs=(n.ttlMinutes??ec)*60*1e3;let r=(n.baseUrl??"").replace(/\/+$/,"").toLowerCase();if(this.key=r?`${r}|${Xr}`:Xr,n.persistDir){this.persistence={directory:n.persistDir,fileName:tc,ttlMs:this.ttlMs,...n.onWarn?{onWarn:n.onWarn}:{}};let s=Rt(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&&Pt(this.cache?[{key:this.key,value:this.cache.entries,storedAt:this.cache.timestamp}]:[],this.persistence);}};var Cn=0;function Jr(t){let e=t.docnum??`${Date.now().toString().padStart(13,"0")}${String(Cn++).padStart(3,"0")}`;Cn>=1e3&&(Cn=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 nc(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 rc(t,e){return t.filter(n=>n.name===e).length}function Zr(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=rc(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]??"",d=nc(s.name,o,a);d&&n.push(d);}s.children&&s.children.length>0&&Zr(s.children,e,n);}}function Qr(t,e){if(!e)return [];let n=new Map;for(let s of e.segments)n.set(s.name,s);let r=[];return Zr(t,n,r),r}var ic=new XMLBuilder({ignoreAttributes:false,attributeNamePrefix:"@_",format:true,suppressEmptyNode:false});function es(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=es(n),s=e[n.name];s?Array.isArray(s)?s.push(r):e[n.name]=[s,r]:e[n.name]=r;}return e}function ts(t,e,n){let r={};for(let i of n){let o=es(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 ic.build(s)}var kt=class{constructor(e,n,r){this.httpClient=e;this.sapClient=n;this.partnerDefaults=r;}async send(e){let n={...this.partnerDefaults,...e.partnerOverrides},r=Jr({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=Qr(e.segments,e.metadata);if(u.length>0)return {success:false,docnum:r.DOCNUM,message:"Validation failed",violations:u}}let s=ts(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 c=a.match(/<title>(.*?)<\/title>/)?.[1]??"IDoc send failed";return {success:false,docnum:r.DOCNUM,message:c}}};var _t={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","sap_media_upload","sap_media_download"],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"]},Ke=new Set(["sap_create","sap_update","sap_delete","sap_batch","sap_media_upload","sap_idoc_send","sap_rfc_call","sap_bapi_call","sap_bapi_commit"]),Rn=["core","odata"],ns=new Map;for(let[t,e]of Object.entries(_t))for(let n of e)ns.set(n,t);var Ot=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=_t[e],r=[];for(let s of n){if(this.options.readOnly&&Ke.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 _t[e]}isReadOnlyMode(){return this.options.readOnly}shouldToolBeVisible(e){let n=ns.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&&Ke.has(e)):true}};function rs(t){let e=t.tiers??Rn,n=[];for(let r of e)if(!(r==="idoc"&&!t.hasIdocConfig)&&!(r==="rfc"&&!t.hasRfcConfig))for(let s of _t[r])Ke.has(s)&&n.push(s);return n}var ss="0.0.0-unknown",is="guniweb-sap-mcp";function lc(t){if(typeof t!="string"||t.length===0)return ss;let e=resolve(t);for(;;){try{let r=readFileSync(join(e,"package.json"),"utf-8"),s=JSON.parse(r);if(s.name===is&&typeof s.version=="string")return s.version}catch{}let n=dirname(e);if(n===e)return ss;e=n;}}var K=lc(import.meta.dirname),os=is;var uc=/^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})(-.*)?$/,as=/^0+$/;function Pn(t){if(typeof t!="string")return;let e=t.trim();if(e.length>512)return;let n=uc.exec(e);if(!n)return;let[,r,s,i,,o]=n;if(r!=="ff"&&!(r==="00"&&o!==void 0)&&!(as.test(s)||as.test(i)))return {traceId:s,parentId:i}}function cs(t){if(!(!t||typeof t!="object")){for(let[e,n]of Object.entries(t))if(e.toLowerCase()==="traceparent")return Array.isArray(n)?void 0:Pn(n)}}var It=30,pc={authKind:"stdio",personal:false};function mc(t,e){return Ke.has(t)||(e.mutatingCalls?.count??0)>0?"write":t==="sap_function"&&e.sapCalls.count===0?"unknown":"read"}var fc=5e3;function gc(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 d=Date.now()-n;i({method:"notifications/progress",params:{progressToken:s,progress:Math.round(d/1e3),...r.budgetMs?{total:Math.round(r.budgetMs/1e3)}:{},message:`${e.toolName}: ${Math.round(d/1e3)} s, ${e.sapCalls.count} SAP-Aufruf(e)`}}).catch(()=>{});},r.afterMs);return a.unref?.(),()=>clearInterval(a)}var hc="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 yc(t){return {...t,content:[...t.content,{type:"text",text:`\u26A0\uFE0F ${hc}`}]}}function Sc(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 bc(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 ds(t,e,n={}){let{toolTimeoutMs:r,logger:s,tlsVerifyDisabled:i,governance:o}=n,a=n.progressAfterMs??fc;return async(d,l)=>{let c=Qn(),{signal:u,deadline:p}=rr(l?.signal,r),m=cs(l?.requestInfo?.headers)??Pn(l?._meta?.traceparent),f={correlationId:c,...m?{traceId:m.traceId}:{},toolName:t,signal:u,deadline:p,sapCalls:{count:0},mutatingCalls:{count:0},created:{}},g=Date.now();return er(f,async()=>{let h=gc(l,f,g,{afterMs:a,...r?{budgetMs:r}:{}}),b="ok",y,E=(async()=>e(d,l))();E.catch(()=>{});try{y=await Promise.race([E,wc(u,t)]),y?.isError&&(b="error");}catch(T){if(T instanceof F)b=T.timedOut?"timeout":"aborted",y=T.timedOut?Sc(t,c,Date.now()-g,f.sapCalls.count,r??0):bc(t,c);else {b="error";let x=T instanceof Error?T.message:String(T);y={content:[{type:"text",text:JSON.stringify({error:x,tool:t,correlationId:c})}],isError:true};}}b==="ok"&&u?.aborted&&(b=u.reason?.name==="TimeoutError"?"timeout":"aborted"),h();let S=i?yc(y):y;if(s?.info({correlationId:c,...m?{traceId:m.traceId,parentId:m.parentId}:{},tool:t,outcome:b,durationMs:Date.now()-g,sapCalls:f.sapCalls.count,resultBytes:vc(y),...r?{budgetMs:r}:{}},"MCP tool call"),o){let T={apiVersion:1,serverVersion:n.serverVersion??K,correlationId:c,...m?{traceId:m.traceId,parentId:m.parentId}:{},tool:t,kind:mc(t,f),outcome:b,startedAt:new Date(g).toISOString(),durationMs:Date.now()-g,sapCalls:f.sapCalls.count,...n.destination?{destination:n.destination}:{},...b==="ok"&&f.created?.entity?{createdEntity:f.created.entity}:{},identity:n.callerIdentity??pc,args:d,result:y};try{o.onToolCall(T);}catch(x){s?.error({err:x,correlationId:c,tool:t,module:o.name},"Governance-Modul: Aufruf konnte nicht gemeldet werden");}}return S})}}function wc(t,e){return t?new Promise((n,r)=>{let s=()=>{let i=t.reason?.name==="TimeoutError";r(new F(i,e));};if(t.aborted){s();return}t.addEventListener("abort",s,{once:true});}):new Promise(()=>{})}function vc(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 Tc=1440*60*1e3,Ac="metadata-cache.json",I=class{cache=new Map;ttlMs;baseUrl;persistence;constructor(e={}){if(this.ttlMs=e.ttlMs??Tc,this.baseUrl=Ec(e.baseUrl),e.persistDir){this.persistence={directory:e.persistDir,fileName:Ac,ttlMs:this.ttlMs,...e.onWarn?{onWarn:e.onWarn}:{}};for(let n of Rt(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&&Pt([...this.cache.entries()].map(([e,n])=>({key:e,value:n.value,storedAt:n.storedAt})),this.persistence);}};function Ec(t){return t?t.replace(/\/+$/,"").toLowerCase():""}function ls(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.
|
|
@@ -167,7 +167,7 @@ Use the \`sap_nl_query\` tool with these parameters:
|
|
|
167
167
|
- "orders over 1000 EUR" \u2192 \`{ field: "TotalAmount", operator: "gt", value: 1000 }\`
|
|
168
168
|
- "products containing 'bolt'" \u2192 \`{ field: "ProductName", operator: "contains", value: "bolt" }\`
|
|
169
169
|
- "invoices since January 2024" \u2192 \`{ field: "InvoiceDate", operator: "ge", value: "2024-01-01" }\`
|
|
170
|
-
${i}`}}]}});}function
|
|
170
|
+
${i}`}}]}});}function us(t){t.registerPrompt("explore-sales-orders",{title:"Explore Sales Orders",description:"Discover and query SAP Sales Order data step by step",argsSchema:{serviceUrl:z$2.string().optional().describe("OData service URL (leave empty to discover first)")}},async({serviceUrl:e})=>({messages:[{role:"user",content:{type:"text",text:`I want to explore SAP Sales Order data. Please help me step by step:
|
|
171
171
|
|
|
172
172
|
1. ${e?`Start by getting metadata for the service at: ${e}`:"Start by listing available services. A common Sales Order service URL is /sap/opu/odata/sap/API_SALES_ORDER_SRV"}
|
|
173
173
|
2. Use sap_list_services to discover available entity sets
|
|
@@ -195,21 +195,21 @@ Use sap_query with appropriate filters to find the material data.`}}]})),t.regis
|
|
|
195
195
|
1. ${r}
|
|
196
196
|
2. Use sap_list_services to discover entity sets
|
|
197
197
|
3. Use sap_get_metadata to understand the Business Partner data model
|
|
198
|
-
${s}`}}]}});}var xt=class{cache=new Map;buildKey(e,n){let s=Object.entries(n).sort(([i],[o])=>i.localeCompare(o)).map(([i,o])=>`${i}=${o}`).join(",");return `${e}|${s}`}set(e,n,r){this.cache.set(this.buildKey(e,n),r);}get(e,n){return this.cache.get(this.buildKey(e,n))}update(e,n,r){r?this.set(e,n,r):this.cache.delete(this.buildKey(e,n));}};function
|
|
198
|
+
${s}`}}]}});}var xt=class{cache=new Map;buildKey(e,n){let s=Object.entries(n).sort(([i],[o])=>i.localeCompare(o)).map(([i,o])=>`${i}=${o}`).join(",");return `${e}|${s}`}set(e,n,r){this.cache.set(this.buildKey(e,n),r);}get(e,n){return this.cache.get(this.buildKey(e,n))}update(e,n,r){r?this.set(e,n,r):this.cache.delete(this.buildKey(e,n));}};function Cc(t){let e=t.match(/HTTP\/1\.\d\s+(\d+)\s+/);if(!e)return null;let n=Number.parseInt(e[1],10),r=n>=200&&n<300,s=t.indexOf(e[0]),i=t.substring(s),o=i.indexOf(`
|
|
199
199
|
|
|
200
|
-
`),a;o!==-1&&(a=i.substring(o+2).trim());let d={status:n,entitySet:""};if(a&&a.length>0)if(r)try{d.result=JSON.parse(a);}catch{}else d.error=a;return d}function
|
|
200
|
+
`),a;o!==-1&&(a=i.substring(o+2).trim());let d={status:n,entitySet:""};if(a&&a.length>0)if(r)try{d.result=JSON.parse(a);}catch{}else d.error=a;return d}function ps(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 d=o[1],l=i.indexOf(`
|
|
201
201
|
|
|
202
|
-
`);if(l!==-1){let c=i.substring(l+2);n.push(
|
|
203
|
-
`);return
|
|
204
|
-
`)}function
|
|
205
|
-
${
|
|
206
|
-
${
|
|
202
|
+
`);if(l!==-1){let c=i.substring(l+2);n.push(ps(c,d).flat());}continue}let a=Cc(i);a&&n.push(a);}return n}function ms(t,e){let n=e.match(/boundary=([^\s;]+)/);if(!n)return [];let r=n[1],s=t.replace(/\r\n/g,`
|
|
203
|
+
`);return ps(s,r)}function fe(t,e){let n=t?.entitySets.find(i=>i.name===e),r=n&&t?.entityTypes.find(i=>i.name===n.entityTypeName);if(!r)return;let s={};for(let i of [...r.properties,...r.keys])s[i.name]=i.type;return s}function kn(t,e){let n=t?.functionImports?.find(r=>r.name===e);if(n)return Object.fromEntries(n.parameters.map(r=>[r.name,r.type]))}function fs(t,e){let n=e.slice(e.lastIndexOf(".")+1),r=t?.actions?.find(s=>s.name.slice(s.name.lastIndexOf(".")+1)===n);if(r)return Object.fromEntries(r.parameters.map(s=>[s.name,s.type]))}var Rc=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,Pc=/^(guid|datetime|datetimeoffset|time|binary|X)'[^']*'$/i,kc=/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/;function Mt(t,e){if(t==null)return "null";if(e){let r=_c(t,e);if(r!==void 0)return r}if(typeof t=="boolean"||typeof t=="number"||typeof t=="bigint")return String(t);if(t instanceof Date)return `datetime'${hs(t.toISOString())}'`;let n=String(t);return Pc.test(n)?n:Rc.test(n)?`guid'${n}'`:gs(n)}function _c(t,e){let n=t instanceof Date?t.toISOString():String(t);switch(e){case "Edm.String":return gs(n);case "Edm.Guid":return `guid'${ze(n,"guid")}'`;case "Edm.Boolean":{let r=n.toLowerCase();return r==="true"||r==="false"?r:void 0}case "Edm.DateTime":return `datetime'${hs(ze(n,"datetime"))}'`;case "Edm.DateTimeOffset":return `datetimeoffset'${ze(n,"datetimeoffset")}'`;case "Edm.Time":return `time'${ze(n,"time")}'`;case "Edm.Binary":return `binary'${ze(n,"binary")}'`;case "Edm.Decimal":return Ye(n,"M");case "Edm.Int64":return Ye(n,"L");case "Edm.Double":return Ye(n,"d");case "Edm.Single":return Ye(n,"f");case "Edm.Byte":case "Edm.SByte":case "Edm.Int16":case "Edm.Int32":return Ye(n,"");default:return}}function gs(t){return `'${t.replace(/'/g,"''")}'`}function ze(t,e){let n=new RegExp(`^${e}'(.*)'$`,"is").exec(t);return n?n[1]:t}function Ye(t,e){let n=t.replace(/[mMlLdDfF]$/,"");return kc.test(n)?`${n}${e}`:void 0}function hs(t){let e=/^\/Date\((-?\d+)(?:[+-]\d{4})?\)\/$/.exec(t),n=e?new Date(Number(e[1])).toISOString():t;return /^\d{4}-\d{2}-\d{2}$/.test(n)&&(n=`${n}T00:00:00`),n.replace(/(?:Z|[+-]00:?00)$/i,"").replace(/\.0+$/,"")}function ys(t,e){return Mt(t,e)}function Oe(t,e,n){let r=Object.entries(e);if(r.length===1){let[i,o]=r[0];return `${t}(${ys(o,n?.[i])})`}let s=r.map(([i,o])=>`${i}=${ys(o,n?.[i])}`);return `${t}(${s.join(",")})`}function _n(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 Oc(t,e){let n=[],r=new Map;for(let s of t){if(s.method==="get"){n.push({kind:"single",operations:[s]});continue}if(!s.changesetId){n.push({kind:"changeset",operations:[s]});continue}let i=r.get(s.changesetId);i||(i={kind:"changeset",operations:[]},r.set(s.changesetId,i),n.push(i)),i.operations.push(s);}for(let s of e??[])n.push({kind:"changeset",operations:s.operations});return n}function bs(t,e){let n=t.key?Oe(t.entitySet,t.key,e?.(t.entitySet)):t.entitySet,r=t.method==="patch"?"MERGE":t.method.toUpperCase(),{__etag:s,...i}=t.data??{},o=t.data!==void 0&&(r==="POST"||r==="MERGE")?JSON.stringify(i):void 0,a=["Content-Type: application/http","Content-Transfer-Encoding: binary","",`${r} ${n} HTTP/1.1`,"Accept: application/json"];return (r==="MERGE"||r==="DELETE")&&a.push(`If-Match: ${typeof s=="string"?s:"*"}`),o!==void 0&&a.push("Content-Type: application/json"),a.push("",o??""),a.join(`\r
|
|
204
|
+
`)}function ws(t,e,n,r){let s=`batch_${randomUUID()}`,i=Oc(e,n),o=[];for(let a of i){if(a.kind==="single"){o.push(`--${s}\r
|
|
205
|
+
${bs(a.operations[0],r)}`);continue}let d=`changeset_${randomUUID()}`,l=[...a.operations.map(c=>`--${d}\r
|
|
206
|
+
${bs(c,r)}`),`--${d}--`].join(`\r
|
|
207
207
|
`);o.push(`--${s}\r
|
|
208
208
|
Content-Type: multipart/mixed; boundary=${d}\r
|
|
209
209
|
\r
|
|
210
210
|
${l}`);}return o.push(`--${s}--`),{body:`${o.join(`\r
|
|
211
211
|
`)}\r
|
|
212
|
-
`,contentType:`multipart/mixed; boundary=${s}`,parts:i}}var kc=new Set(["EntityType","Property","NavigationProperty","Association","EntitySet","AssociationSet","PropertyRef","End","FunctionImport","Parameter"]),_c=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",removeNSPrefix:true,processEntities:false,htmlEntities:false,isArray:t=>kc.has(t)});function On(t){return t===void 0?true:String(t).toLowerCase()!=="false"}function Ut(t,e){let r=_c.parse(t).Edmx.DataServices.Schema,s=Array.isArray(r)?r[0]:r,i=s.EntityType??[],o=s.Association??[],a=s.EntityContainer,d=a?.EntitySet??[],l=new Map;for(let g of o)l.set(g["@_Name"],g);let c=i.map(g=>{let h=Oc(g.Property??[]),b=(g.Key?.PropertyRef??[]).map(S=>S["@_Name"]),y=h.filter(S=>b.includes(S.name)),E=Ic(g.NavigationProperty??[],l);return {name:g["@_Name"],keys:y,properties:h,navigationProperties:E}}),u=d.map(g=>({name:g["@_Name"],entityTypeName:In(g["@_EntityType"]),creatable:On(g["@_creatable"]),updatable:On(g["@_updatable"]),deletable:On(g["@_deletable"])})),p=a?.FunctionImport??[],m=Nc(Array.isArray(p)?p:[p]),f={version:"v2",serviceUrl:e,entityTypes:c,entitySets:u};return m.length>0&&(f.functionImports=m),f}function Oc(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 Ic(t,e){return t.map(n=>{let r=In(n["@_Relationship"]),s=e.get(r),i="Unknown",o=false;if(s){let a=s.End.find(d=>d["@_Role"]===n["@_ToRole"]);a&&(i=In(a["@_Type"]),o=a["@_Multiplicity"]==="*");}return {name:n["@_Name"],targetEntityType:i,isCollection:o}})}function Nc(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 In(t){let e=t.lastIndexOf(".");return e>=0?t.substring(e+1):t}var Dc=/^\/Date\((-?\d+)([+-]\d{4})?\)\/$/;function se(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 ws(t){let e=Dc.exec(t);if(!e)return t;let n=Number(e[1]);return new Date(n).toISOString()}function K(t){if(t==null)return t;if(Array.isArray(t))return t.map(e=>K(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]=ws(s):n[r]=K(s)));return n}return typeof t=="string"?ws(t):t}var Ts=10,Xe=class{constructor(e,n,r,s){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;this.metadataProvider=s;}async metadataForLiterals(){if(this.metadataProvider)try{return await this.metadataProvider()}catch{return}}async keyUrl(e,n){let r=fe(await this.metadataForLiterals(),e);return `${this.serviceUrl}/${Oe(e,n,r)}`}async read(e,n,r){let s=_n(r??{});if(n){let i=await this.keyUrl(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=se(o.data);return {...a,results:a.results.map(d=>K(d))}}return this.fetchWithPagination(`${this.serviceUrl}/${e}`,s,r?.maxPages??Ts)}async query(e,n){let r=_n(n,true),s=n.maxPages??Ts;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=r.data?.d;ct(s?.__metadata?.uri??Le(r.headers,"location"));let i=se(r.data);return i.results.length>0?K(i.results[0]):{}}async update(e,n,r){let s=await this.keyUrl(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=await this.keyUrl(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=await this.metadataForLiterals(),i=s?.functionImports?.find(f=>f.name===e),o=kn(s,e),a=(r??i?.httpMethod??"POST").toLowerCase(),d={$format:"json"};if(n)for(let[f,g]of Object.entries(n))d[f]=Mt(g,o?.[f]);let l=await this.httpClient.request({method:a,url:`${this.serviceUrl}/${e}`,headers:{Accept:"application/json"},params:d}),c=l.data?.d;if(i?.returnType?.startsWith("Collection(")||Array.isArray(c)||Array.isArray(c?.results)){let f=Array.isArray(c)?c:se(l.data).results;return {results:K(f)}}let p=se(l.data);if(p.results.length>0)return K(p.results[0]);let m=l.data;return m?.d&&typeof m.d=="object"?m.d:{}}async batch(e,n){let r=await this.metadataForLiterals(),{body:s,contentType:i,parts:o}=bs(this.serviceUrl,e,n,m=>fe(r,m)),a=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:s,headers:{"Content-Type":i,Accept:"multipart/mixed"}}),d=a.headers["content-type"]??i,l=ps(a.data,d),c=new Map;return o.forEach((m,f)=>{let g=l[f];m.operations.forEach((h,b)=>{let y=Array.isArray(g)?g[b]:g;c.set(h,y?{...y,entitySet:h.entitySet}:{status:0,entitySet:h.entitySet,error:"SAP returned no response for this operation"});});}),{results:[...e,...(n??[]).flatMap(m=>m.operations)].map(m=>c.get(m)).filter(m=>m!==void 0)}}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml"}});return Ut(e.data,this.serviceUrl)}async fetchWithPagination(e,n,r){let s=[],i,o=e,a=n,d=0,l=false;for(;o&&d<r;){if(d>0&&dt()){l=true;break}let p=(await this.httpClient.request({method:"get",url:o,headers:{Accept:"application/json"},params:a})).data,m=se(p),f=m.results.map(h=>K(h));s.push(...f),d===0&&m.count!==void 0&&(i=m.count),d++;let g=p?.d;g&&typeof g.__next=="string"?(o=g.__next,a=void 0):o=void 0;}let c={results:s};return i!==void 0&&(c.count=i),o&&(c.nextLink=o),l&&(c.truncated=true,c.truncationReason=`Nach ${d} Seite(n) abgebrochen: Zeitbudget des Tool-Aufrufs ersch\xF6pft oder Client hat die Verbindung verworfen. Mit skip=${s.length} weiterlesen.`),c}};function vs(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})}var xc=/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/,Mc=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,Uc=/(Z|[+-]\d{2}:?\d{2})$/i;function Lt(t,e){if(t==null)return "null";if(e){let n=$c(t,e);if(n!==void 0)return n}return typeof t=="boolean"||typeof t=="number"||typeof t=="bigint"?String(t):t instanceof Date?t.toISOString():Es(String(t))}function $c(t,e){let n=t instanceof Date?t.toISOString():String(t);switch(e){case "Edm.String":return Es(n);case "Edm.Guid":{let r=$t($t(n,"guid"),"");return Mc.test(r)?r:void 0}case "Edm.Boolean":{let r=n.toLowerCase();return r==="true"||r==="false"?r:void 0}case "Edm.Date":{let r=As(n);return /^\d{4}-\d{2}-\d{2}/.test(r)?r.slice(0,10):void 0}case "Edm.DateTimeOffset":{let r=As(n);return /^\d{4}-\d{2}-\d{2}$/.test(r)&&(r=`${r}T00:00:00`),/^\d{4}-\d{2}-\d{2}T/.test(r)?Uc.test(r)?r:`${r}Z`:void 0}case "Edm.TimeOfDay":return /^\d{2}:\d{2}(:\d{2}(\.\d+)?)?$/.test(n)?n:void 0;case "Edm.Duration":return `duration'${$t(n,"duration")}'`;case "Edm.Binary":return `binary'${$t(n,"binary")}'`;case "Edm.Decimal":case "Edm.Double":case "Edm.Single":case "Edm.Int64":case "Edm.Int32":case "Edm.Int16":case "Edm.Byte":case "Edm.SByte":{let r=n.replace(/[mMlLdDfF]$/,"");return xc.test(r)?r:void 0}default:return}}function Es(t){return `'${t.replace(/'/g,"''")}'`}function $t(t,e){let n=new RegExp(`^${e}'(.*)'$`,"is").exec(t);return n?n[1]:t}function As(t){let e=/^\/Date\((-?\d+)(?:[+-]\d{4})?\)\/$/.exec(t);return e?new Date(Number(e[1])).toISOString():t}function Cs(t,e){return Lt(t,e)}function Bt(t,e,n){let r=Object.entries(e);if(r.length===1){let[i,o]=r[0];return `${t}(${Cs(o,n?.[i])})`}let s=r.map(([i,o])=>`${i}=${Cs(o,n?.[i])}`).join(",");return `${t}(${s})`}function Ft(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 Rs(t,e,n){let r=[];for(let i=0;i<e.length;i++){let o=e[i],a=o.key?Bt(o.entitySet,o.key,n?.(o.entitySet)):o.entitySet,d={id:String(i+1),method:o.method.toUpperCase(),url:a};o.changesetId&&(d.atomicityGroup=o.changesetId),o.data&&(o.method==="post"||o.method==="patch"||o.method==="delete")&&(d.headers={"Content-Type":"application/json"},d.body=o.data),r.push(d);}return {body:JSON.stringify({requests:r}),contentType:"application/json"}}function Ps(t){let e=t.split(".");return e[e.length-1]}function Lc(t,e){return {name:t,type:e.$Type??"Edm.String",nullable:e.$Nullable??false,maxLength:e.$MaxLength,precision:e.$Precision,scale:e.$Scale}}function ks(t,e){let n=JSON.parse(t),r=[],s=[],i=[];for(let[a,d]of Object.entries(n)){if(a.startsWith("$"))continue;let l=d;for(let[c,u]of Object.entries(l)){let p=u;if(p.$Kind==="Action"||p.$Kind==="Function"){let m=p.$IsBound??false,f=p.$Parameter??[],g=m&&f.length>0?f[0].$Name:void 0,h=(m?f.slice(1):f).map(y=>({name:y.$Name,type:y.$Type??"Edm.String",...y.$Nullable===true?{nullable:true}:{}})),b=p.$ReturnType?p.$ReturnType.$Type:void 0;i.push({name:c,isBound:m,kind:p.$Kind,...g?{bindingParameter:g}:{},parameters:h,...b?{returnType:b}:{}});}else if(p.$Kind==="EntityType"){let m=[],f=[];for(let[b,y]of Object.entries(p)){if(b.startsWith("$"))continue;let E=y;E.$Kind==="NavigationProperty"?f.push({name:b,targetEntityType:Ps(E.$Type),isCollection:E.$Collection??false}):m.push(Lc(b,E));}let h=(p.$Key??[]).map(b=>{let y=m.find(E=>E.name===b);return y||{name:b,type:"Edm.String",nullable:false}});r.push({name:c,keys:h,properties:m,navigationProperties:f});}else if(p.$Kind==="EntityContainer")for(let[m,f]of Object.entries(p)){if(m.startsWith("$"))continue;let g=f;g.$Collection&&g.$Type&&s.push({name:m,entityTypeName:Ps(g.$Type),creatable:Nn(g,"InsertRestrictions","Insertable"),updatable:Nn(g,"UpdateRestrictions","Updatable"),deletable:Nn(g,"DeleteRestrictions","Deletable")});}}}let o={version:"v4",serviceUrl:e,entityTypes:r,entitySets:s};return i.length>0&&(o.actions=i),o}function Nn(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 Fc=new Set(["Schema","EntityType","Property","NavigationProperty","PropertyRef","EntityContainer","EntitySet","Action","Function","Parameter","Annotations","Annotation","PropertyValue"]),Hc=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",removeNSPrefix:true,processEntities:false,htmlEntities:false,isArray:t=>Fc.has(t)});function H(t){return Array.isArray(t)?t:[]}function N(t,e){let n=t?.[`@_${e}`];return n===void 0?void 0:String(n)}function Dn(t,e){let n=N(t,e);return n===void 0||n==="max"?void 0:Number.parseInt(n,10)}function Os(t){let e=/^Collection\((.*)\)$/.exec(t)?.[1]??t;return e.slice(e.lastIndexOf(".")+1)}function jc(t){let e={name:N(t,"Name"),type:N(t,"Type")??"Edm.String",nullable:N(t,"Nullable")!=="false"},n=Dn(t,"MaxLength"),r=Dn(t,"Precision"),s=Dn(t,"Scale");return n!==void 0&&(e.maxLength=n),r!==void 0&&(e.precision=r),s!==void 0&&(e.scale=s),e}function Vc(t){let e=H(t.Property).map(jc),r=H(t.Key?.PropertyRef).map(i=>N(i,"Name")).map(i=>e.find(o=>o.name===i)??{name:i??"",type:"Edm.String",nullable:false}),s=H(t.NavigationProperty).map(i=>{let o=N(i,"Type")??"";return {name:N(i,"Name"),targetEntityType:Os(o),isCollection:o.startsWith("Collection(")}});return {name:N(t,"Name"),keys:r,properties:e,navigationProperties:s}}function _s(t,e){let n=N(t,"IsBound")==="true",r=H(t.Parameter),s=n?N(r[0],"Name"):void 0,i=(n?r.slice(1):r).map(a=>({name:N(a,"Name"),type:N(a,"Type")??"Edm.String",...N(a,"Nullable")!=="false"?{nullable:true}:{}})),o=N(t.ReturnType,"Type");return {name:N(t,"Name"),isBound:n,kind:e,...s?{bindingParameter:s}:{},parameters:i,...o?{returnType:o}:{}}}function xn(t,e,n){for(let r of t){if(!N(r,"Term")?.endsWith(e))continue;let i=H(r.Record?.PropertyValue).find(a=>N(a,"Property")===n),o=N(i,"Bool")??(i?.Bool!==void 0?String(i.Bool):void 0);if(o!==void 0)return o==="true"}}function Is(t,e){let r=Hc.parse(t).Edmx?.DataServices;if(!r)throw new Error("V4 metadata: <edmx:DataServices> fehlt \u2014 kein g\xFCltiges EDMX-Dokument");let s=H(r.Schema),i=[],o=[],a=[],d=new Map;for(let u of s){i.push(...H(u.EntityType).map(Vc)),o.push(...H(u.Action).map(p=>_s(p,"Action"))),o.push(...H(u.Function).map(p=>_s(p,"Function")));for(let p of H(u.EntityContainer))a.push(...H(p.EntitySet));for(let p of H(u.Annotations)){let m=N(p,"Target")??"",f=m.lastIndexOf("/");if(f===-1)continue;let g=m.slice(f+1);d.set(g,[...d.get(g)??[],...H(p.Annotation)]);}}let l=a.map(u=>{let p=N(u,"Name"),m=[...H(u.Annotation),...d.get(p)??[]];return {name:p,entityTypeName:Os(N(u,"EntityType")??""),creatable:xn(m,"InsertRestrictions","Insertable")??true,updatable:xn(m,"UpdateRestrictions","Updatable")??true,deletable:xn(m,"DeleteRestrictions","Deletable")??true}}),c={version:"v4",serviceUrl:e,entityTypes:i,entitySets:l};return o.length>0&&(c.actions=o),c}var Wc=10,Ht=class{constructor(e,n,r,s){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;this.metadataProvider=s;}async metadataForLiterals(){if(this.metadataProvider)try{return await this.metadataProvider()}catch{return}}async keyUrl(e,n){let r=fe(await this.metadataForLiterals(),e);return `${this.serviceUrl}/${Bt(e,n,r)}`}async read(e,n,r){if(n){let l=await this.keyUrl(e,n),c=r?Ft(r):void 0,u=await this.httpClient.request({method:"get",url:l,params:c}),p=u.data,m=u.headers.etag??u.headers.ETag;return m&&this.etagCache.set(e,n,m),{results:[p]}}let s=r?Ft(r):void 0,o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=o.value??[],d=o["@odata.count"];return {results:a,count:d}}async query(e,n){let r=n.maxPages??Wc,s=Ft(n,true),o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=[...o.value??[]],d=o["@odata.count"],l=o["@odata.nextLink"],c=1,u=false;for(;l&&c<r;){if(dt()){u=true;break}let m=this.resolveNextLink(l),g=(await this.httpClient.request({method:"get",url:m})).data,h=g.value??[];a.push(...h),l=g["@odata.nextLink"],c++;}let p={results:a,count:d};return l&&(p.nextLink=l),u&&(p.truncated=true,p.truncationReason=`Nach ${c} Seite(n) abgebrochen: Zeitbudget des Tool-Aufrufs ersch\xF6pft oder Client hat die Verbindung verworfen. Mit skip=${a.length} weiterlesen.`),p}async create(e,n){let r=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/${e}`,data:n,headers:{"Content-Type":"application/json"}});return ct(Le(r.headers,"location")??Le(r.headers,"odata-entityid")??r.data?.["@odata.id"]),r.data}async update(e,n,r){let s=await this.keyUrl(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=await this.keyUrl(e,n),s=this.etagCache.get(e,n)??"*";await this.httpClient.request({method:"delete",url:r,headers:{"If-Match":s}});}async batch(e){let n=await this.metadataForLiterals(),{body:r,contentType:s}=Rs(this.serviceUrl,e,a=>fe(n,a)),i=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:r,headers:{"Content-Type":s,Accept:"application/json"}}),o=vs(i.data);for(let a=0;a<o.length&&a<e.length;a++)o[a].entitySet=e[a].entitySet;return {results:o}}async callAction(e,n,r,s){let i;return r&&s?i=`${await this.keyUrl(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=ms(await this.metadataForLiterals(),e),o=this.buildFunctionParams(n,i),a;return r&&s?a=`${await this.keyUrl(r,s)}/${e}(${o})`:a=`${this.serviceUrl}/${e}(${o})`,(await this.httpClient.request({method:"get",url:a})).data??{}}buildFunctionParams(e,n){return !e||Object.keys(e).length===0?"":Object.entries(e).map(([r,s])=>`${r}=${Lt(s,n?.[r])}`).join(",")}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml"}}),n=typeof e.data=="string"?e.data:JSON.stringify(e.data);return Mn(n,this.serviceUrl)}resolveNextLink(e){return e.startsWith("http://")||e.startsWith("https://")?e:`${this.serviceUrl}/${e}`}};function Mn(t,e){return t.trimStart().startsWith("{")?ks(t,e):Is(t,e)}function Un(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 /<(?:edmx:)?Edmx\b[^>]*\bVersion\s*=\s*["']([^"']+)["']/i.exec(n)?.[1]?.startsWith("4")?"v4":"v2";if(e){if(e.includes("/odata4/"))return "v4";if(e.includes("/odata/"))return "v2"}return "v2"}var $=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.metadataCache=r??new I;}protocolClient=null;version=null;initPromise=null;etagCache=new xt;metadataCache;metadataUnavailable=false;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=Un(n,this.serviceUrl),r=!0;}catch{this.version=Un("",this.serviceUrl),this.metadataUnavailable=true;}if(this.protocolClient=this.createProtocolClient(this.version),r&&n)try{let s=this.version==="v4"?Mn(n,this.serviceUrl):Ut(n,this.serviceUrl);this.metadataCache.set(this.serviceUrl,s);}catch{this.metadataUnavailable=true;}}async metadataForLiterals(){if(!this.metadataUnavailable)try{return await this.getMetadata()}catch{this.metadataUnavailable=true;return}}createProtocolClient(e){return e==="v4"?new Ht(this.httpClient,this.serviceUrl,this.etagCache,()=>this.metadataForLiterals()):new Xe(this.httpClient,this.serviceUrl,this.etagCache,()=>this.metadataForLiterals())}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 Xe)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 Ns(t,e,n){let r=n??new I,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),d=decodeURIComponent(o.entitySet),c=await new $(e,a,r).getMetadata(),u=c.entitySets.find(h=>h.name===d);if(!u)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity set '${d}' not found in service ${a}`,availableEntitySets:c.entitySets.map(h=>h.name)},null,2)}]};let p=c.entityTypes.find(h=>h.name===u.entityTypeName);if(!p)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity type '${u.entityTypeName}' not found in metadata`},null,2)}]};let m=new Set(p.keys.map(h=>h.name)),f=p.properties.map(h=>({name:h.name,type:h.type,isKey:m.has(h.name)})),g=qc(p.properties,c.version);return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({entitySet:d,version:c.version,properties:f,sampleFilter:g},null,2)}]}});}function qc(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 Ds(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 xs(t,e,n){let r=n??new I,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 $(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 $n={"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 Yc={$TODAY:()=>Ln(new Date),$YESTERDAY:()=>{let t=new Date;return t.setDate(t.getDate()-1),Ln(t)},$MONTH_START:()=>{let t=new Date;return `${t.getFullYear()}-${Bn(t.getMonth()+1)}-01`},$YEAR_START:()=>`${new Date().getFullYear()}-01-01`,$MONTH_AGO:()=>{let t=new Date;return t.setMonth(t.getMonth()-1),Ln(t)}};function Ln(t){return `${t.getFullYear()}-${Bn(t.getMonth()+1)}-${Bn(t.getDate())}`}function Bn(t){return t.toString().padStart(2,"0")}function Xc(t){if(typeof t!="string")return t;let e=Yc[t];return e?e():t}function Ms(t){return {...t,queryTemplates:t.queryTemplates.map(e=>({...e,filters:e.filters.map(n=>({...n,value:Xc(n.value)}))}))}}function Us(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=$n[s];if(!i)return {contents:[{uri:n.toString(),mimeType:"application/json",text:JSON.stringify({error:`Unknown domain '${s}'`,availableDomains:Object.keys($n)},null,2)}]};let o=Ms(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 $s="/sap/opu/odata/sap",Zc=/^[A-Z0-9_/]+$/i,ge=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 ${$s}/API_BUSINESS_PARTNER oder der technische Name (API_BUSINESS_PARTNER), den sap_discover_services als technicalName liefert.`;}};function j(t){let e=t.trim();if(e.length===0)throw new ge(t);if(e.startsWith("/"))return e.replace(/\/+$/,"")||"/";if(e.startsWith("http://")||e.startsWith("https://"))try{return new URL(e).pathname.replace(/\/+$/,"")}catch{throw new ge(t)}if(Zc.test(e))return `${$s}/${e.replace(/^\/+|\/+$/g,"")}`;throw new ge(t)}var Je=class extends Error{guidance;extra;constructor(e,n,r={}){super(e),this.name="ToolInputError",this.guidance=n,this.extra=r;}};function _(t,e){if(t instanceof ge)return Fn({code:"INVALID_INPUT",error:t.message,guidance:t.guidance,...e?.hint?{hint:e.hint}:{}});if(t instanceof Je)return Fn({code:"INVALID_INPUT",error:t.message,...t.guidance?{guidance:t.guidance}:{},...t.extra,...e?.hint?{hint:e.hint}:{}});let n=be(t,{serviceUrl:e?.serviceUrl});return Fn({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 Fn(t){let e=ye()?.correlationId;return {content:[{type:"text",text:JSON.stringify({...t,...e?{correlationId:e}:{}},null,2)}],isError:true}}function Ls(t,e,n){let r=n??new I,s=new Map;function i(a){let d=j(a),l=s.get(d);return l||(l=new $(e,d,r),s.set(d,l)),l}function o(a,d){return _(a,{serviceUrl:d})}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. Write operations sharing a changesetId run as one transaction (all succeed or all fail); in V2 a write without changesetId gets a changeset of its own. Returns per-operation status in the order of the operations -- 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:d})=>{try{let l=i(a),c=d,u=await l.batch(c);return {content:[{type:"text",text:JSON.stringify(u,null,2)}]}}catch(l){return o(l)}});}async function Bs(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 Qc=[".integrationsuite.","integrationsuite-trial.",".it-cpi.",".hci.",".prod.apimanagement.","-api.s4hana.ondemand.com"];function Fs(t){let e=t.toLowerCase();return Qc.some(n=>e.includes(n))}var J=class extends Error{constructor(n,r,s,i){super(n);this.httpStatus=r;this.sapGuidance=s;this.isBtpDetected=i;this.name="CatalogError";}},ed=new Set(["ECONNREFUSED","ETIMEDOUT","ENOTFOUND"]);function td(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 nd(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 Hs(t){return typeof t=="object"&&t!==null&&"response"in t&&typeof t.response=="object"&&t.response!==null&&typeof t.response.status=="number"}function rd(t){return typeof t=="object"&&t!==null&&"code"in t&&typeof t.code=="string"&&ed.has(t.code)}function Ze(t,e,n=false){let r=Fs(e);if(Hs(t)){let i=t.response.status,o=td(i,n),a="message"in t&&typeof t.message=="string"?t.message:`HTTP ${i}`;return new J(a,i,o,r)}if(rd(t)){let i=nd(t.code),o="message"in t&&typeof t.message=="string"?t.message:t.code;return new J(o,void 0,i,r)}let s=t instanceof Error?t.message:String(t);return new J(s,void 0,void 0,r)}function js(t){return Hs(t)&&t.response.status===404?"V4 catalog not available. Publish service group via /IWFND/V4_ADMIN.":t instanceof Error?t.message:String(t)}var sd=["/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/iwfnd/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/IWFND/CATALOGSERVICE/ServiceCollection"];function id(t){if(typeof t=="object"&&t!==null&&"response"in t)return t.response?.status}async function Vs(t,e,n){let r;for(let s of sd)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 F)throw i;if(id(i)===404){r=i;continue}throw Ze(i,e,false)}throw Ze(r??new Error("All V2 catalog paths returned 404"),e,false)}function Ws(t){try{return new URL(t).pathname}catch{return t}}function ad(t){let e=t.toLowerCase().replace(/'/g,"''");return [`substringof('${e}',tolower(TechnicalServiceName))`,`substringof('${e}',tolower(Title))`,`substringof('${e}',tolower(Description))`].join(" or ")}var jt=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;serverSideFilterUnsupported=false;async discoverServices(e){let n=this.cache.get();if(n)return {services:n,warnings:[]};if(e&&!this.serverSideFilterUnsupported){let l=await this.tryFilteredFetch(e);if(l)return l}let r=[],[s,i]=await Bs(["v4","v2"],async l=>l==="v4"?await this.fetchV4Catalog().then(c=>({ok:true,services:c}),c=>({ok:false,error:c})):await this.fetchV2Catalog().then(c=>({ok:true,services:c}),c=>({ok:false,error:c})),this.options.concurrency??5),o=[];if(s?.ok)o=s.services;else if(s){if(s.error instanceof F)throw s.error;r.push(`V4 catalog fetch failed: ${js(s.error)}`);}if(!i?.ok){let l=i?.error;throw l instanceof J?l:Ze(l,this.options.baseUrl??"",false)}let a=i.services,d=this.mergeAndDeduplicate(o,a);return this.cache.set(d),{services:d,warnings:r}}async tryFilteredFetch(e){try{return {services:await this.fetchV2Catalog(ad(e)),warnings:[],filtered:!0}}catch(n){if(n instanceof F)throw n;let r=n?.response?.status;(r===400||r===501)&&(this.serverSideFilterUnsupported=true);return}}async fetchV2Catalog(e){let n=this.options.catalogV2Url??this.resolvedV2Path;return n||(n=await Vs(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:Ws(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 d of a)i.push({technicalName:d.ServiceId??d.ServiceName??"",title:d.ServiceTitle??d.Title??"",serviceUrl:Ws(d.ServiceUrl),version:"v4",serviceVersion:d.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 cd=[[/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"]],dd=[[/^[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 ld(t){for(let[e,n]of cd)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};for(let[e,n]of dd)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};return {...t,category:"unknown",categoryConfidence:"low"}}function Gs(t){return t.map(ld)}function Ie(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 Ne(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 qs(t,e,n,r){let s=r??new re,i=new jt(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
|
+
`,contentType:`multipart/mixed; boundary=${s}`,parts:i}}var Nc=new Set(["EntityType","Property","NavigationProperty","Association","EntitySet","AssociationSet","PropertyRef","End","FunctionImport","Parameter"]),Dc=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",removeNSPrefix:true,processEntities:false,htmlEntities:false,isArray:t=>Nc.has(t)});function On(t){return t===void 0?true:String(t).toLowerCase()!=="false"}function Ut(t,e){let r=Dc.parse(t).Edmx.DataServices.Schema,s=Array.isArray(r)?r[0]:r,i=s.EntityType??[],o=s.Association??[],a=s.EntityContainer,d=a?.EntitySet??[],l=new Map;for(let g of o)l.set(g["@_Name"],g);let c=i.map(g=>{let h=xc(g.Property??[]),b=(g.Key?.PropertyRef??[]).map(S=>S["@_Name"]),y=h.filter(S=>b.includes(S.name)),E=Mc(g.NavigationProperty??[],l);return {name:g["@_Name"],keys:y,properties:h,navigationProperties:E}}),u=d.map(g=>({name:g["@_Name"],entityTypeName:In(g["@_EntityType"]),creatable:On(g["@_creatable"]),updatable:On(g["@_updatable"]),deletable:On(g["@_deletable"])})),p=a?.FunctionImport??[],m=Uc(Array.isArray(p)?p:[p]),f={version:"v2",serviceUrl:e,entityTypes:c,entitySets:u};return m.length>0&&(f.functionImports=m),f}function xc(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 Mc(t,e){return t.map(n=>{let r=In(n["@_Relationship"]),s=e.get(r),i="Unknown",o=false;if(s){let a=s.End.find(d=>d["@_Role"]===n["@_ToRole"]);a&&(i=In(a["@_Type"]),o=a["@_Multiplicity"]==="*");}return {name:n["@_Name"],targetEntityType:i,isCollection:o}})}function Uc(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 In(t){let e=t.lastIndexOf(".");return e>=0?t.substring(e+1):t}var $c=/^\/Date\((-?\d+)([+-]\d{4})?\)\/$/;function se(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 vs(t){let e=$c.exec(t);if(!e)return t;let n=Number(e[1]);return new Date(n).toISOString()}function z(t){if(t==null)return t;if(Array.isArray(t))return t.map(e=>z(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]=vs(s):n[r]=z(s)));return n}return typeof t=="string"?vs(t):t}var Ts=10,Xe=class{constructor(e,n,r,s){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;this.metadataProvider=s;}async metadataForLiterals(){if(this.metadataProvider)try{return await this.metadataProvider()}catch{return}}async keyUrl(e,n){let r=fe(await this.metadataForLiterals(),e);return `${this.serviceUrl}/${Oe(e,n,r)}`}async read(e,n,r){let s=_n(r??{});if(n){let i=await this.keyUrl(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=se(o.data);return {...a,results:a.results.map(d=>z(d))}}return this.fetchWithPagination(`${this.serviceUrl}/${e}`,s,r?.maxPages??Ts)}async query(e,n){let r=_n(n,true),s=n.maxPages??Ts;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=r.data?.d;ct(s?.__metadata?.uri??Le(r.headers,"location"));let i=se(r.data);return i.results.length>0?z(i.results[0]):{}}async update(e,n,r){let s=await this.keyUrl(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=await this.keyUrl(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=await this.metadataForLiterals(),i=s?.functionImports?.find(f=>f.name===e),o=kn(s,e),a=(r??i?.httpMethod??"POST").toLowerCase(),d={$format:"json"};if(n)for(let[f,g]of Object.entries(n))d[f]=Mt(g,o?.[f]);let l=await this.httpClient.request({method:a,url:`${this.serviceUrl}/${e}`,headers:{Accept:"application/json"},params:d}),c=l.data?.d;if(i?.returnType?.startsWith("Collection(")||Array.isArray(c)||Array.isArray(c?.results)){let f=Array.isArray(c)?c:se(l.data).results;return {results:z(f)}}let p=se(l.data);if(p.results.length>0)return z(p.results[0]);let m=l.data;return m?.d&&typeof m.d=="object"?m.d:{}}async batch(e,n){let r=await this.metadataForLiterals(),{body:s,contentType:i,parts:o}=ws(this.serviceUrl,e,n,m=>fe(r,m)),a=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:s,headers:{"Content-Type":i,Accept:"multipart/mixed"}}),d=a.headers["content-type"]??i,l=ms(a.data,d),c=new Map;return o.forEach((m,f)=>{let g=l[f];m.operations.forEach((h,b)=>{let y=Array.isArray(g)?g[b]:g;c.set(h,y?{...y,entitySet:h.entitySet}:{status:0,entitySet:h.entitySet,error:"SAP returned no response for this operation"});});}),{results:[...e,...(n??[]).flatMap(m=>m.operations)].map(m=>c.get(m)).filter(m=>m!==void 0)}}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml"}});return Ut(e.data,this.serviceUrl)}async fetchWithPagination(e,n,r){let s=[],i,o=e,a=n,d=0,l=false;for(;o&&d<r;){if(d>0&&dt()){l=true;break}let p=(await this.httpClient.request({method:"get",url:o,headers:{Accept:"application/json"},params:a})).data,m=se(p),f=m.results.map(h=>z(h));s.push(...f),d===0&&m.count!==void 0&&(i=m.count),d++;let g=p?.d;g&&typeof g.__next=="string"?(o=g.__next,a=void 0):o=void 0;}let c={results:s};return i!==void 0&&(c.count=i),o&&(c.nextLink=o),l&&(c.truncated=true,c.truncationReason=`Nach ${d} Seite(n) abgebrochen: Zeitbudget des Tool-Aufrufs ersch\xF6pft oder Client hat die Verbindung verworfen. Mit skip=${s.length} weiterlesen.`),c}};function As(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})}var Lc=/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/,Bc=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,Fc=/(Z|[+-]\d{2}:?\d{2})$/i;function Lt(t,e){if(t==null)return "null";if(e){let n=Hc(t,e);if(n!==void 0)return n}return typeof t=="boolean"||typeof t=="number"||typeof t=="bigint"?String(t):t instanceof Date?t.toISOString():Cs(String(t))}function Hc(t,e){let n=t instanceof Date?t.toISOString():String(t);switch(e){case "Edm.String":return Cs(n);case "Edm.Guid":{let r=$t($t(n,"guid"),"");return Bc.test(r)?r:void 0}case "Edm.Boolean":{let r=n.toLowerCase();return r==="true"||r==="false"?r:void 0}case "Edm.Date":{let r=Es(n);return /^\d{4}-\d{2}-\d{2}/.test(r)?r.slice(0,10):void 0}case "Edm.DateTimeOffset":{let r=Es(n);return /^\d{4}-\d{2}-\d{2}$/.test(r)&&(r=`${r}T00:00:00`),/^\d{4}-\d{2}-\d{2}T/.test(r)?Fc.test(r)?r:`${r}Z`:void 0}case "Edm.TimeOfDay":return /^\d{2}:\d{2}(:\d{2}(\.\d+)?)?$/.test(n)?n:void 0;case "Edm.Duration":return `duration'${$t(n,"duration")}'`;case "Edm.Binary":return `binary'${$t(n,"binary")}'`;case "Edm.Decimal":case "Edm.Double":case "Edm.Single":case "Edm.Int64":case "Edm.Int32":case "Edm.Int16":case "Edm.Byte":case "Edm.SByte":{let r=n.replace(/[mMlLdDfF]$/,"");return Lc.test(r)?r:void 0}default:return}}function Cs(t){return `'${t.replace(/'/g,"''")}'`}function $t(t,e){let n=new RegExp(`^${e}'(.*)'$`,"is").exec(t);return n?n[1]:t}function Es(t){let e=/^\/Date\((-?\d+)(?:[+-]\d{4})?\)\/$/.exec(t);return e?new Date(Number(e[1])).toISOString():t}function Rs(t,e){return Lt(t,e)}function Bt(t,e,n){let r=Object.entries(e);if(r.length===1){let[i,o]=r[0];return `${t}(${Rs(o,n?.[i])})`}let s=r.map(([i,o])=>`${i}=${Rs(o,n?.[i])}`).join(",");return `${t}(${s})`}function Ft(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 Ps(t,e,n){let r=[];for(let i=0;i<e.length;i++){let o=e[i],a=o.key?Bt(o.entitySet,o.key,n?.(o.entitySet)):o.entitySet,d={id:String(i+1),method:o.method.toUpperCase(),url:a};o.changesetId&&(d.atomicityGroup=o.changesetId),o.data&&(o.method==="post"||o.method==="patch"||o.method==="delete")&&(d.headers={"Content-Type":"application/json"},d.body=o.data),r.push(d);}return {body:JSON.stringify({requests:r}),contentType:"application/json"}}function ks(t){let e=t.split(".");return e[e.length-1]}function jc(t,e){return {name:t,type:e.$Type??"Edm.String",nullable:e.$Nullable??false,maxLength:e.$MaxLength,precision:e.$Precision,scale:e.$Scale}}function _s(t,e){let n=JSON.parse(t),r=[],s=[],i=[];for(let[a,d]of Object.entries(n)){if(a.startsWith("$"))continue;let l=d;for(let[c,u]of Object.entries(l)){let p=u;if(p.$Kind==="Action"||p.$Kind==="Function"){let m=p.$IsBound??false,f=p.$Parameter??[],g=m&&f.length>0?f[0].$Name:void 0,h=(m?f.slice(1):f).map(y=>({name:y.$Name,type:y.$Type??"Edm.String",...y.$Nullable===true?{nullable:true}:{}})),b=p.$ReturnType?p.$ReturnType.$Type:void 0;i.push({name:c,isBound:m,kind:p.$Kind,...g?{bindingParameter:g}:{},parameters:h,...b?{returnType:b}:{}});}else if(p.$Kind==="EntityType"){let m=[],f=[];for(let[b,y]of Object.entries(p)){if(b.startsWith("$"))continue;let E=y;E.$Kind==="NavigationProperty"?f.push({name:b,targetEntityType:ks(E.$Type),isCollection:E.$Collection??false}):m.push(jc(b,E));}let h=(p.$Key??[]).map(b=>{let y=m.find(E=>E.name===b);return y||{name:b,type:"Edm.String",nullable:false}});r.push({name:c,keys:h,properties:m,navigationProperties:f});}else if(p.$Kind==="EntityContainer")for(let[m,f]of Object.entries(p)){if(m.startsWith("$"))continue;let g=f;g.$Collection&&g.$Type&&s.push({name:m,entityTypeName:ks(g.$Type),creatable:Nn(g,"InsertRestrictions","Insertable"),updatable:Nn(g,"UpdateRestrictions","Updatable"),deletable:Nn(g,"DeleteRestrictions","Deletable")});}}}let o={version:"v4",serviceUrl:e,entityTypes:r,entitySets:s};return i.length>0&&(o.actions=i),o}function Nn(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 Wc=new Set(["Schema","EntityType","Property","NavigationProperty","PropertyRef","EntityContainer","EntitySet","Action","Function","Parameter","Annotations","Annotation","PropertyValue"]),Gc=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",removeNSPrefix:true,processEntities:false,htmlEntities:false,isArray:t=>Wc.has(t)});function H(t){return Array.isArray(t)?t:[]}function N(t,e){let n=t?.[`@_${e}`];return n===void 0?void 0:String(n)}function Dn(t,e){let n=N(t,e);return n===void 0||n==="max"?void 0:Number.parseInt(n,10)}function Is(t){let e=/^Collection\((.*)\)$/.exec(t)?.[1]??t;return e.slice(e.lastIndexOf(".")+1)}function qc(t){let e={name:N(t,"Name"),type:N(t,"Type")??"Edm.String",nullable:N(t,"Nullable")!=="false"},n=Dn(t,"MaxLength"),r=Dn(t,"Precision"),s=Dn(t,"Scale");return n!==void 0&&(e.maxLength=n),r!==void 0&&(e.precision=r),s!==void 0&&(e.scale=s),e}function Kc(t){let e=H(t.Property).map(qc),r=H(t.Key?.PropertyRef).map(i=>N(i,"Name")).map(i=>e.find(o=>o.name===i)??{name:i??"",type:"Edm.String",nullable:false}),s=H(t.NavigationProperty).map(i=>{let o=N(i,"Type")??"";return {name:N(i,"Name"),targetEntityType:Is(o),isCollection:o.startsWith("Collection(")}});return {name:N(t,"Name"),keys:r,properties:e,navigationProperties:s}}function Os(t,e){let n=N(t,"IsBound")==="true",r=H(t.Parameter),s=n?N(r[0],"Name"):void 0,i=(n?r.slice(1):r).map(a=>({name:N(a,"Name"),type:N(a,"Type")??"Edm.String",...N(a,"Nullable")!=="false"?{nullable:true}:{}})),o=N(t.ReturnType,"Type");return {name:N(t,"Name"),isBound:n,kind:e,...s?{bindingParameter:s}:{},parameters:i,...o?{returnType:o}:{}}}function xn(t,e,n){for(let r of t){if(!N(r,"Term")?.endsWith(e))continue;let i=H(r.Record?.PropertyValue).find(a=>N(a,"Property")===n),o=N(i,"Bool")??(i?.Bool!==void 0?String(i.Bool):void 0);if(o!==void 0)return o==="true"}}function Ns(t,e){let r=Gc.parse(t).Edmx?.DataServices;if(!r)throw new Error("V4 metadata: <edmx:DataServices> fehlt \u2014 kein g\xFCltiges EDMX-Dokument");let s=H(r.Schema),i=[],o=[],a=[],d=new Map;for(let u of s){i.push(...H(u.EntityType).map(Kc)),o.push(...H(u.Action).map(p=>Os(p,"Action"))),o.push(...H(u.Function).map(p=>Os(p,"Function")));for(let p of H(u.EntityContainer))a.push(...H(p.EntitySet));for(let p of H(u.Annotations)){let m=N(p,"Target")??"",f=m.lastIndexOf("/");if(f===-1)continue;let g=m.slice(f+1);d.set(g,[...d.get(g)??[],...H(p.Annotation)]);}}let l=a.map(u=>{let p=N(u,"Name"),m=[...H(u.Annotation),...d.get(p)??[]];return {name:p,entityTypeName:Is(N(u,"EntityType")??""),creatable:xn(m,"InsertRestrictions","Insertable")??true,updatable:xn(m,"UpdateRestrictions","Updatable")??true,deletable:xn(m,"DeleteRestrictions","Deletable")??true}}),c={version:"v4",serviceUrl:e,entityTypes:i,entitySets:l};return o.length>0&&(c.actions=o),c}var zc=10,Ht=class{constructor(e,n,r,s){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;this.metadataProvider=s;}async metadataForLiterals(){if(this.metadataProvider)try{return await this.metadataProvider()}catch{return}}async keyUrl(e,n){let r=fe(await this.metadataForLiterals(),e);return `${this.serviceUrl}/${Bt(e,n,r)}`}async read(e,n,r){if(n){let l=await this.keyUrl(e,n),c=r?Ft(r):void 0,u=await this.httpClient.request({method:"get",url:l,params:c}),p=u.data,m=u.headers.etag??u.headers.ETag;return m&&this.etagCache.set(e,n,m),{results:[p]}}let s=r?Ft(r):void 0,o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=o.value??[],d=o["@odata.count"];return {results:a,count:d}}async query(e,n){let r=n.maxPages??zc,s=Ft(n,true),o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=[...o.value??[]],d=o["@odata.count"],l=o["@odata.nextLink"],c=1,u=false;for(;l&&c<r;){if(dt()){u=true;break}let m=this.resolveNextLink(l),g=(await this.httpClient.request({method:"get",url:m})).data,h=g.value??[];a.push(...h),l=g["@odata.nextLink"],c++;}let p={results:a,count:d};return l&&(p.nextLink=l),u&&(p.truncated=true,p.truncationReason=`Nach ${c} Seite(n) abgebrochen: Zeitbudget des Tool-Aufrufs ersch\xF6pft oder Client hat die Verbindung verworfen. Mit skip=${a.length} weiterlesen.`),p}async create(e,n){let r=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/${e}`,data:n,headers:{"Content-Type":"application/json"}});return ct(Le(r.headers,"location")??Le(r.headers,"odata-entityid")??r.data?.["@odata.id"]),r.data}async update(e,n,r){let s=await this.keyUrl(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=await this.keyUrl(e,n),s=this.etagCache.get(e,n)??"*";await this.httpClient.request({method:"delete",url:r,headers:{"If-Match":s}});}async batch(e){let n=await this.metadataForLiterals(),{body:r,contentType:s}=Ps(this.serviceUrl,e,a=>fe(n,a)),i=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:r,headers:{"Content-Type":s,Accept:"application/json"}}),o=As(i.data);for(let a=0;a<o.length&&a<e.length;a++)o[a].entitySet=e[a].entitySet;return {results:o}}async callAction(e,n,r,s){let i;return r&&s?i=`${await this.keyUrl(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=fs(await this.metadataForLiterals(),e),o=this.buildFunctionParams(n,i),a;return r&&s?a=`${await this.keyUrl(r,s)}/${e}(${o})`:a=`${this.serviceUrl}/${e}(${o})`,(await this.httpClient.request({method:"get",url:a})).data??{}}buildFunctionParams(e,n){return !e||Object.keys(e).length===0?"":Object.entries(e).map(([r,s])=>`${r}=${Lt(s,n?.[r])}`).join(",")}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml"}}),n=typeof e.data=="string"?e.data:JSON.stringify(e.data);return Mn(n,this.serviceUrl)}resolveNextLink(e){return e.startsWith("http://")||e.startsWith("https://")?e:`${this.serviceUrl}/${e}`}};function Mn(t,e){return t.trimStart().startsWith("{")?_s(t,e):Ns(t,e)}function Un(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 /<(?:edmx:)?Edmx\b[^>]*\bVersion\s*=\s*["']([^"']+)["']/i.exec(n)?.[1]?.startsWith("4")?"v4":"v2";if(e){if(e.includes("/odata4/"))return "v4";if(e.includes("/odata/"))return "v2"}return "v2"}var $=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.metadataCache=r??new I;}protocolClient=null;version=null;initPromise=null;etagCache=new xt;metadataCache;metadataUnavailable=false;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=Un(n,this.serviceUrl),r=!0;}catch{this.version=Un("",this.serviceUrl),this.metadataUnavailable=true;}if(this.protocolClient=this.createProtocolClient(this.version),r&&n)try{let s=this.version==="v4"?Mn(n,this.serviceUrl):Ut(n,this.serviceUrl);this.metadataCache.set(this.serviceUrl,s);}catch{this.metadataUnavailable=true;}}async metadataForLiterals(){if(!this.metadataUnavailable)try{return await this.getMetadata()}catch{this.metadataUnavailable=true;return}}createProtocolClient(e){return e==="v4"?new Ht(this.httpClient,this.serviceUrl,this.etagCache,()=>this.metadataForLiterals()):new Xe(this.httpClient,this.serviceUrl,this.etagCache,()=>this.metadataForLiterals())}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 Xe)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 Ds(t,e,n){let r=n??new I,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),d=decodeURIComponent(o.entitySet),c=await new $(e,a,r).getMetadata(),u=c.entitySets.find(h=>h.name===d);if(!u)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity set '${d}' not found in service ${a}`,availableEntitySets:c.entitySets.map(h=>h.name)},null,2)}]};let p=c.entityTypes.find(h=>h.name===u.entityTypeName);if(!p)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity type '${u.entityTypeName}' not found in metadata`},null,2)}]};let m=new Set(p.keys.map(h=>h.name)),f=p.properties.map(h=>({name:h.name,type:h.type,isKey:m.has(h.name)})),g=Xc(p.properties,c.version);return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({entitySet:d,version:c.version,properties:f,sampleFilter:g},null,2)}]}});}function Xc(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 xs(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 Ms(t,e,n){let r=n??new I,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 $(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 $n={"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 Qc={$TODAY:()=>Ln(new Date),$YESTERDAY:()=>{let t=new Date;return t.setDate(t.getDate()-1),Ln(t)},$MONTH_START:()=>{let t=new Date;return `${t.getFullYear()}-${Bn(t.getMonth()+1)}-01`},$YEAR_START:()=>`${new Date().getFullYear()}-01-01`,$MONTH_AGO:()=>{let t=new Date;return t.setMonth(t.getMonth()-1),Ln(t)}};function Ln(t){return `${t.getFullYear()}-${Bn(t.getMonth()+1)}-${Bn(t.getDate())}`}function Bn(t){return t.toString().padStart(2,"0")}function ed(t){if(typeof t!="string")return t;let e=Qc[t];return e?e():t}function Us(t){return {...t,queryTemplates:t.queryTemplates.map(e=>({...e,filters:e.filters.map(n=>({...n,value:ed(n.value)}))}))}}function $s(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=$n[s];if(!i)return {contents:[{uri:n.toString(),mimeType:"application/json",text:JSON.stringify({error:`Unknown domain '${s}'`,availableDomains:Object.keys($n)},null,2)}]};let o=Us(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 Ls="/sap/opu/odata/sap",nd=/^[A-Z0-9_/]+$/i,ge=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 ${Ls}/API_BUSINESS_PARTNER oder der technische Name (API_BUSINESS_PARTNER), den sap_discover_services als technicalName liefert.`;}};function j(t){let e=t.trim();if(e.length===0)throw new ge(t);if(e.startsWith("/"))return e.replace(/\/+$/,"")||"/";if(e.startsWith("http://")||e.startsWith("https://"))try{return new URL(e).pathname.replace(/\/+$/,"")}catch{throw new ge(t)}if(nd.test(e))return `${Ls}/${e.replace(/^\/+|\/+$/g,"")}`;throw new ge(t)}var Je=class extends Error{guidance;extra;constructor(e,n,r={}){super(e),this.name="ToolInputError",this.guidance=n,this.extra=r;}};function _(t,e){if(t instanceof ge)return Fn({code:"INVALID_INPUT",error:t.message,guidance:t.guidance,...e?.hint?{hint:e.hint}:{}});if(t instanceof Je)return Fn({code:"INVALID_INPUT",error:t.message,...t.guidance?{guidance:t.guidance}:{},...t.extra,...e?.hint?{hint:e.hint}:{}});let n=be(t,{serviceUrl:e?.serviceUrl});return Fn({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 Fn(t){let e=ye()?.correlationId;return {content:[{type:"text",text:JSON.stringify({...t,...e?{correlationId:e}:{}},null,2)}],isError:true}}function Bs(t,e,n){let r=n??new I,s=new Map;function i(a){let d=j(a),l=s.get(d);return l||(l=new $(e,d,r),s.set(d,l)),l}function o(a,d){return _(a,{serviceUrl:d})}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. Write operations sharing a changesetId run as one transaction (all succeed or all fail); in V2 a write without changesetId gets a changeset of its own. Returns per-operation status in the order of the operations -- 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:d})=>{try{let l=i(a),c=d,u=await l.batch(c);return {content:[{type:"text",text:JSON.stringify(u,null,2)}]}}catch(l){return o(l)}});}async function Fs(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 rd=[".integrationsuite.","integrationsuite-trial.",".it-cpi.",".hci.",".prod.apimanagement.","-api.s4hana.ondemand.com"];function Hs(t){let e=t.toLowerCase();return rd.some(n=>e.includes(n))}var J=class extends Error{constructor(n,r,s,i){super(n);this.httpStatus=r;this.sapGuidance=s;this.isBtpDetected=i;this.name="CatalogError";}},sd=new Set(["ECONNREFUSED","ETIMEDOUT","ENOTFOUND"]);function id(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 od(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 js(t){return typeof t=="object"&&t!==null&&"response"in t&&typeof t.response=="object"&&t.response!==null&&typeof t.response.status=="number"}function ad(t){return typeof t=="object"&&t!==null&&"code"in t&&typeof t.code=="string"&&sd.has(t.code)}function Ze(t,e,n=false){let r=Hs(e);if(js(t)){let i=t.response.status,o=id(i,n),a="message"in t&&typeof t.message=="string"?t.message:`HTTP ${i}`;return new J(a,i,o,r)}if(ad(t)){let i=od(t.code),o="message"in t&&typeof t.message=="string"?t.message:t.code;return new J(o,void 0,i,r)}let s=t instanceof Error?t.message:String(t);return new J(s,void 0,void 0,r)}function Vs(t){return js(t)&&t.response.status===404?"V4 catalog not available. Publish service group via /IWFND/V4_ADMIN.":t instanceof Error?t.message:String(t)}var cd=["/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/iwfnd/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/IWFND/CATALOGSERVICE/ServiceCollection"];function dd(t){if(typeof t=="object"&&t!==null&&"response"in t)return t.response?.status}async function Ws(t,e,n){let r;for(let s of cd)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 F)throw i;if(dd(i)===404){r=i;continue}throw Ze(i,e,false)}throw Ze(r??new Error("All V2 catalog paths returned 404"),e,false)}function Gs(t){try{return new URL(t).pathname}catch{return t}}function ud(t){let e=t.toLowerCase().replace(/'/g,"''");return [`substringof('${e}',tolower(TechnicalServiceName))`,`substringof('${e}',tolower(Title))`,`substringof('${e}',tolower(Description))`].join(" or ")}var jt=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;serverSideFilterUnsupported=false;async discoverServices(e){let n=this.cache.get();if(n)return {services:n,warnings:[]};if(e&&!this.serverSideFilterUnsupported){let l=await this.tryFilteredFetch(e);if(l)return l}let r=[],[s,i]=await Fs(["v4","v2"],async l=>l==="v4"?await this.fetchV4Catalog().then(c=>({ok:true,services:c}),c=>({ok:false,error:c})):await this.fetchV2Catalog().then(c=>({ok:true,services:c}),c=>({ok:false,error:c})),this.options.concurrency??5),o=[];if(s?.ok)o=s.services;else if(s){if(s.error instanceof F)throw s.error;r.push(`V4 catalog fetch failed: ${Vs(s.error)}`);}if(!i?.ok){let l=i?.error;throw l instanceof J?l:Ze(l,this.options.baseUrl??"",false)}let a=i.services,d=this.mergeAndDeduplicate(o,a);return this.cache.set(d),{services:d,warnings:r}}async tryFilteredFetch(e){try{return {services:await this.fetchV2Catalog(ud(e)),warnings:[],filtered:!0}}catch(n){if(n instanceof F)throw n;let r=n?.response?.status;(r===400||r===501)&&(this.serverSideFilterUnsupported=true);return}}async fetchV2Catalog(e){let n=this.options.catalogV2Url??this.resolvedV2Path;return n||(n=await Ws(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:Gs(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 d of a)i.push({technicalName:d.ServiceId??d.ServiceName??"",title:d.ServiceTitle??d.Title??"",serviceUrl:Gs(d.ServiceUrl),version:"v4",serviceVersion:d.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 pd=[[/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"]],md=[[/^[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 fd(t){for(let[e,n]of pd)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};for(let[e,n]of md)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};return {...t,category:"unknown",categoryConfidence:"low"}}function qs(t){return t.map(fd)}function Ie(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 Ne(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 Ks(t,e,n,r){let s=r??new re,i=new jt(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.
|
|
213
213
|
|
|
214
214
|
**When to use:**
|
|
215
215
|
- You need to find which SAP services are available on this system
|
|
@@ -218,7 +218,7 @@ ${l}`);}return o.push(`--${s}--`),{body:`${o.join(`\r
|
|
|
218
218
|
|
|
219
219
|
**When NOT to use:**
|
|
220
220
|
- You already know the service URL (proceed to sap_query or sap_nl_query)
|
|
221
|
-
- You need entity-level metadata (use sap_get_metadata after discovering the service)`,inputSchema:z$2.object({search:z$2.string().optional().describe("Free-text search across service name, title, and description (case-insensitive)"),category:z$2.enum(["business-partner","sales","finance","procurement","hr","logistics","production","quality","plant-maintenance","project","material","warehouse","analytics","master-data","cross-application","unknown"]).optional().describe("Filter by business domain category"),limit:z$2.number().optional().default(50).describe(`Maximum number of services to return (default ${50}, hard cap ${1e3})`),offset:z$2.number().optional().default(0).describe("Number of services to skip for pagination (default 0)")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({search:o,category:a,limit:d,offset:l})=>{try{let c=await i.discoverServices(o),u=
|
|
221
|
+
- You need entity-level metadata (use sap_get_metadata after discovering the service)`,inputSchema:z$2.object({search:z$2.string().optional().describe("Free-text search across service name, title, and description (case-insensitive)"),category:z$2.enum(["business-partner","sales","finance","procurement","hr","logistics","production","quality","plant-maintenance","project","material","warehouse","analytics","master-data","cross-application","unknown"]).optional().describe("Filter by business domain category"),limit:z$2.number().optional().default(50).describe(`Maximum number of services to return (default ${50}, hard cap ${1e3})`),offset:z$2.number().optional().default(0).describe("Number of services to skip for pagination (default 0)")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({search:o,category:a,limit:d,offset:l})=>{try{let c=await i.discoverServices(o),u=qs(c.services);if(a&&(u=u.filter(y=>y.category===a)),o&&!c.filtered){let y=o.toLowerCase();u=u.filter(E=>E.technicalName.toLowerCase().includes(y)||E.title.toLowerCase().includes(y)||(E.description?.toLowerCase().includes(y)??!1));}let p=u.length,m={};for(let y of u)m[y.category]=(m[y.category]??0)+1;let f=Ie(d,l),g=u.slice(f.skip,f.skip+f.top),h=Ne({returned:g.length,totalCount:p,paging:f}),b={services:g.map(y=>({technicalName:y.technicalName,title:y.title,category:y.category,serviceUrl:y.serviceUrl,version:y.version})),...h,summary:{total:p,returned:g.length,offset:f.skip,byCategory:m},...c.warnings.length>0?{warnings:c.warnings}:{},...o?{filterApplied:c.filtered?"server":"client"}:{}};return {content:[{type:"text",text:JSON.stringify(b,null,2)}]}}catch(c){if(c instanceof J){let u={error:c.message};return c.sapGuidance&&(u.guidance=c.sapGuidance),c.isBtpDetected&&(u.btpWarning="BTP Integration Suite detected. Catalog discovery requires direct S/4HANA access. The Integration Suite proxies individual OData services but does not expose the system catalog. Use sap_list_services with a known service URL instead."),{content:[{type:"text",text:JSON.stringify(u,null,2)}],isError:true}}return _(c)}});}function zs(t,e,n){let r=new I;t.registerTool("sap_list_services",{title:"List SAP Entity Sets",description:`Discover available entity sets in a specific SAP OData service. Requires a full OData service path \u2014 NOT a base URL. Common SAP OData services: /sap/opu/odata/sap/API_BUSINESS_PARTNER, /sap/opu/odata/sap/API_MATERIAL_DOCUMENT_SRV, /sap/opu/odata/sap/API_SALES_ORDER_SRV. Use test-connection first to verify SAP connectivity.
|
|
222
222
|
|
|
223
223
|
**When to use:**
|
|
224
224
|
- You need the raw list of OData services at a base URL
|
|
@@ -226,7 +226,7 @@ ${l}`);}return o.push(`--${s}--`),{body:`${o.join(`\r
|
|
|
226
226
|
|
|
227
227
|
**When NOT to use:**
|
|
228
228
|
- Use sap_discover_services instead for categorized, searchable results
|
|
229
|
-
- You already know the service URL`,inputSchema:z$2.object({serviceUrl:z$2.string().describe('Full OData service path starting with /sap/opu/odata/sap/. Example: /sap/opu/odata/sap/API_BUSINESS_PARTNER. Do NOT pass "/" or a base URL.'),top:z$2.number().optional().describe(`Maximum number of entity sets to return (default ${50}, hard cap ${1e3}).`),skip:z$2.number().optional().describe("Number of entity sets to skip for paging")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:s,top:i,skip:o})=>{try{let a=j(s),l=(await e.request({method:"get",url:a,headers:{Accept:"application/json"},params:{$format:"json"}})).data,c=[],u=l.d;u&&"EntitySets"in u&&Array.isArray(u.EntitySets)?c=u.EntitySets:"value"in l&&Array.isArray(l.value)&&(c=l.value.map(h=>h.name)),c=c.filter(h=>
|
|
229
|
+
- You already know the service URL`,inputSchema:z$2.object({serviceUrl:z$2.string().describe('Full OData service path starting with /sap/opu/odata/sap/. Example: /sap/opu/odata/sap/API_BUSINESS_PARTNER. Do NOT pass "/" or a base URL.'),top:z$2.number().optional().describe(`Maximum number of entity sets to return (default ${50}, hard cap ${1e3}).`),skip:z$2.number().optional().describe("Number of entity sets to skip for paging")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:s,top:i,skip:o})=>{try{let a=j(s),l=(await e.request({method:"get",url:a,headers:{Accept:"application/json"},params:{$format:"json"}})).data,c=[],u=l.d;u&&"EntitySets"in u&&Array.isArray(u.EntitySets)?c=u.EntitySets:"value"in l&&Array.isArray(l.value)&&(c=l.value.map(h=>h.name)),c=c.filter(h=>Y(h,n));let p=Ie(i,o),m=c.length,f=c.slice(p.skip,p.skip+p.top),g=Ne({returned:f.length,totalCount:m,paging:p});return {content:[{type:"text",text:JSON.stringify({serviceUrl:a,entitySets:f,count:m,...g},null,2)}]}}catch(a){return _(a,{serviceUrl:s})}}),t.registerTool("sap_get_metadata",{title:"Get SAP Service Metadata",description:`Inspect entity types, properties, keys, and navigation properties for an SAP OData service. Second step: after listing services, use this to understand the data model before executing operations.
|
|
230
230
|
|
|
231
231
|
Each entity set carries what SAP allows on it \u2014 \`creatable\`, \`updatable\`, \`deletable\` \u2014 plus a \`capabilities\` summary listing the entity sets you can create in. Use it to answer questions like 'which service can create sales orders?' without reading raw $metadata XML.
|
|
232
232
|
|
|
@@ -237,24 +237,24 @@ Each entity set carries what SAP allows on it \u2014 \`creatable\`, \`updatable\
|
|
|
237
237
|
- Use 'search' to filter large services by name pattern
|
|
238
238
|
|
|
239
239
|
**When NOT to use:**
|
|
240
|
-
- You don't know which service to query (use sap_discover_services first)`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entityType:z$2.string().optional().describe("Filter to a specific entity type name for full property details"),search:z$2.string().optional().describe("Filter entity types/sets by name pattern (case-insensitive). Use this for large services to narrow results.")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:s,entityType:i,search:o})=>{try{let d=await new $(e,j(s),r).getMetadata(),l;if(i){let c=d.entityTypes.find(u=>u.name===i);if(!c){let u=i.toLowerCase();c=d.entityTypes.find(p=>p.name.toLowerCase()===u);}if(!c){let u=i.toLowerCase(),p=d.entityTypes.filter(m=>m.name.toLowerCase().includes(u)).map(m=>m.name).slice(0,20);return _(new Je(`Entity type '${i}' existiert in diesem Service nicht.`,"Mit dem Parameter 'search' nach passenden Typen suchen, oder entityType weglassen, um alle Entity Sets zu sehen.",p.length>0?{closestMatches:p}:{}),{serviceUrl:s})}l={version:d.version,entityType:c};}else {let c=d.entitySets.filter(f=>
|
|
240
|
+
- You don't know which service to query (use sap_discover_services first)`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entityType:z$2.string().optional().describe("Filter to a specific entity type name for full property details"),search:z$2.string().optional().describe("Filter entity types/sets by name pattern (case-insensitive). Use this for large services to narrow results.")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:s,entityType:i,search:o})=>{try{let d=await new $(e,j(s),r).getMetadata(),l;if(i){let c=d.entityTypes.find(u=>u.name===i);if(!c){let u=i.toLowerCase();c=d.entityTypes.find(p=>p.name.toLowerCase()===u);}if(!c){let u=i.toLowerCase(),p=d.entityTypes.filter(m=>m.name.toLowerCase().includes(u)).map(m=>m.name).slice(0,20);return _(new Je(`Entity type '${i}' existiert in diesem Service nicht.`,"Mit dem Parameter 'search' nach passenden Typen suchen, oder entityType weglassen, um alle Entity Sets zu sehen.",p.length>0?{closestMatches:p}:{}),{serviceUrl:s})}l={version:d.version,entityType:c};}else {let c=d.entitySets.filter(f=>Y(f.name,n)),u=d.entityTypes;if(o){let f=o.toLowerCase();c=c.filter(g=>g.name.toLowerCase().includes(f)),u=u.filter(g=>g.name.toLowerCase().includes(f));}let p={creatable:c.filter(f=>f.creatable).map(f=>f.name),updatable:c.filter(f=>f.updatable).length,deletable:c.filter(f=>f.deletable).length};u.length>50&&!o?l={version:d.version,entitySets:c,capabilities:p,entityTypeCount:u.length,hint:`Service has ${u.length} entity types. Use the 'search' parameter (e.g. search='BusinessPartner') or 'entityType' parameter to get details for specific types. Entity sets listed above are queryable.`}:l={version:d.version,entityTypes:u.map(f=>({name:f.name,keys:f.keys.map(g=>g.name),propertyCount:f.properties.length,navigationProperties:f.navigationProperties.map(g=>g.name)})),entitySets:c,capabilities:p};}return {content:[{type:"text",text:JSON.stringify(l,null,2)}]}}catch(a){return _(a,{serviceUrl:s})}});}function Xs(t,e){if(!t)return "unknown";let n=Ys(e).toLowerCase(),r=t.functionImports?.find(i=>i.name.toLowerCase()===n);if(r)return r.httpMethod==="GET"?"read":"write";let s=t.actions?.find(i=>Ys(i.name).toLowerCase()===n);return s?.kind==="Function"?"read":s?.kind==="Action"?"write":"unknown"}function Ys(t){let e=t.lastIndexOf(".");return e===-1?t:t.slice(e+1)}function Js(t,e,n,r=false){let s=n??new I,i=new Map;function o(l){let c=j(l),u=i.get(c);return u||(u=new $(e,c,s),i.set(c,u)),u}function a(l,c){return _(l,{serviceUrl:c})}async function d(l,c){if(!r)return null;let u;try{u=await o(l).getMetadata();}catch{}let p=Xs(u,c);if(p==="read")return null;let m=p==="write"?`"${c}" schreibt laut den Metadaten des Dienstes (POST).`:`"${c}" ist in den Metadaten des Dienstes nicht als lesend nachweisbar.`;return _(new Error(`${m} Dieser Zugang ist lesend. Lesende Funktionsimporte (V2 HttpMethod=GET, V4 Function) sind erlaubt; f\xFCr schreibende braucht es einen Zugang ohne Read-only-Policy bzw. einen Server mit --allow-write.`),{serviceUrl:l})}t.registerTool("sap_function",{title:"Call SAP Function Import or Action",description:`Invoke an OData V2 Function Import or V4 Action/Function. For V2: calls function imports with parameters; values are typed from $metadata (Edm.Guid \u2192 guid'\u2026', Edm.Boolean \u2192 true/false), so pass plain values. For V4: supports bound actions (on a specific entity) and unbound actions/functions; keys and function parameters are typed from $metadata too (Edm.Guid and Edm.Date bare, strings quoted). Set isFunction=true for V4 Functions (GET), leave false for V4 Actions (POST). For bound operations, provide entitySet and key.
|
|
241
241
|
|
|
242
|
-
Fiori draft services (V2, e.g. purchase requisitions): activate a draft with the <EntitySet>Activation function import and start editing an active document with <EntitySet>Edit, passing the full key as parameters, e.g. { PurchaseRequisition: "", DraftUUID: "<uuid>", IsActiveEntity: false }. An active document has DraftUUID 00000000-0000-0000-0000-000000000000 and IsActiveEntity true. Draft items have a DraftUUID of their own: read their keys via the draft header's navigation (e.g. to_PurchaseReqnItem) before sap_update.`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),functionName:z$2.string().describe("Function import name (V2) or action/function name (V4). For V4 bound operations, include the full namespace e.g. com.sap.namespace.ActionName"),parameters:z$2.record(z$2.string(),z$2.unknown()).optional().describe("Function/action parameters as key-value pairs"),httpMethod:z$2.enum(["GET","POST"]).optional().describe("HTTP method override. V2: from metadata or default POST. V4: Actions=POST, Functions=GET"),entitySet:z$2.string().optional().describe("Entity set for V4 bound actions/functions"),key:z$2.record(z$2.string(),z$2.unknown()).optional().describe('Entity key for V4 bound actions/functions, e.g. { BusinessPartner: "1000001" }'),isFunction:z$2.boolean().optional().describe("Set true for V4 Functions (GET). Default false = Action (POST). Ignored for V2.")}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:false,openWorldHint:true}},async({serviceUrl:l,functionName:c,parameters:u,httpMethod:p,entitySet:m,key:f,isFunction:g})=>{try{let h=await d(l,c);if(h)return h;let y=await o(l).functionImport(c,u,{httpMethod:p,entitySet:m,key:f,isFunction:g??!1});return {content:[{type:"text",text:JSON.stringify(y,null,2)}]}}catch(h){return a(h)}});}function
|
|
242
|
+
Fiori draft services (V2, e.g. purchase requisitions): activate a draft with the <EntitySet>Activation function import and start editing an active document with <EntitySet>Edit, passing the full key as parameters, e.g. { PurchaseRequisition: "", DraftUUID: "<uuid>", IsActiveEntity: false }. An active document has DraftUUID 00000000-0000-0000-0000-000000000000 and IsActiveEntity true. Draft items have a DraftUUID of their own: read their keys via the draft header's navigation (e.g. to_PurchaseReqnItem) before sap_update.`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),functionName:z$2.string().describe("Function import name (V2) or action/function name (V4). For V4 bound operations, include the full namespace e.g. com.sap.namespace.ActionName"),parameters:z$2.record(z$2.string(),z$2.unknown()).optional().describe("Function/action parameters as key-value pairs"),httpMethod:z$2.enum(["GET","POST"]).optional().describe("HTTP method override. V2: from metadata or default POST. V4: Actions=POST, Functions=GET"),entitySet:z$2.string().optional().describe("Entity set for V4 bound actions/functions"),key:z$2.record(z$2.string(),z$2.unknown()).optional().describe('Entity key for V4 bound actions/functions, e.g. { BusinessPartner: "1000001" }'),isFunction:z$2.boolean().optional().describe("Set true for V4 Functions (GET). Default false = Action (POST). Ignored for V2.")}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:false,openWorldHint:true}},async({serviceUrl:l,functionName:c,parameters:u,httpMethod:p,entitySet:m,key:f,isFunction:g})=>{try{let h=await d(l,c);if(h)return h;let y=await o(l).functionImport(c,u,{httpMethod:p,entitySet:m,key:f,isFunction:g??!1});return {content:[{type:"text",text:JSON.stringify(y,null,2)}]}}catch(h){return a(h)}});}function Zs(t,e,n){t.registerTool("test-connection",{title:"Test SAP Connection",description:`Diagnose the connection to the configured SAP system stage by stage: DNS \u2192 TLS (including certificate chain) \u2192 authentication \u2192 client (Mandant) \u2192 service catalog. Reports the FIRST failing stage with a concrete next step, so a 401 no longer leaves you guessing between certificate, user and client.
|
|
243
243
|
|
|
244
244
|
**When to use:**
|
|
245
245
|
- Any SAP tool fails and you need to know which layer is at fault
|
|
246
246
|
- After changing SAP_BASE_URL, credentials, SAP_CLIENT or the CA bundle
|
|
247
247
|
|
|
248
248
|
**When NOT to use:**
|
|
249
|
-
- You already know the connection works (proceed to sap_discover_services)`,inputSchema:z$2.object({}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async()=>{try{let r=await pt(e,{baseUrl:n?.baseUrl??"",sapClient:n?.sapClient,tlsVerify:n?.tlsVerify});return {content:[{type:"text",text:JSON.stringify({...r,...r.status==="connected"?{hint:"N\xE4chster Schritt: sap_discover_services zeigt die verf\xFCgbaren OData-Services dieses Systems."}:{}},null,2)}],...r.status==="failed"?{isError:!0}:{}}}catch(r){return _(r,{hint:"Die Verbindungsdiagnose konnte nicht ausgef\xFChrt werden. Das ist ein Fehler des Servers, kein Befund \xFCber das SAP-System."})}});}function
|
|
249
|
+
- You already know the connection works (proceed to sap_discover_services)`,inputSchema:z$2.object({}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async()=>{try{let r=await pt(e,{baseUrl:n?.baseUrl??"",sapClient:n?.sapClient,tlsVerify:n?.tlsVerify});return {content:[{type:"text",text:JSON.stringify({...r,...r.status==="connected"?{hint:"N\xE4chster Schritt: sap_discover_services zeigt die verf\xFCgbaren OData-Services dieses Systems."}:{}},null,2)}],...r.status==="failed"?{isError:!0}:{}}}catch(r){return _(r,{hint:"Die Verbindungsdiagnose konnte nicht ausgef\xFChrt werden. Das ist ein Fehler des Servers, kein Befund \xFCber das SAP-System."})}});}function hd(t){return {name:t.FieldName,dataType:t.DataType,length:t.Length,decimals:t.Decimals??0,required:t.Required}}function yd(t){return {name:t.SegmentName,parentSegment:t.ParentSegment??"",minOccurrence:t.MinOccurrence,maxOccurrence:t.MaxOccurrence,fields:t.Fields?.results?.map(hd)??[]}}function Sd(t){return {idocType:t.IdocType,mesType:t.MesType,segments:t.Segments?.results?.map(yd)??[]}}async function Hn(t,e,n){try{if(n){let o=(await t.request({method:"get",url:`${e}/IdocTypes('${n}')?$expand=Segments($expand=Fields)`})).data;return Sd(o.d)}return (await t.request({method:"get",url:`${e}/IdocTypes`})).data.d.results.map(i=>({idocType:i.IdocType,mesType:i.MesType}))}catch(r){let s=r instanceof Error?r.message:String(r);throw new Error(`Failed to fetch IDoc metadata: ${s}`)}}var Qs={50:{text:"IDoc added",category:"pending"},51:{text:"Application document not posted",category:"error"},52:{text:"Application document not fully posted",category:"error"},53:{text:"Application document posted",category:"success"},56:{text:"IDoc with errors added",category:"error"},62:{text:"IDoc passed to application",category:"pending"},64:{text:"IDoc ready to be transferred to application",category:"pending"},66:{text:"IDoc is waiting for predecessor IDoc (serialization)",category:"pending"},68:{text:"Error - no further processing",category:"error"},69:{text:"IDoc was edited",category:"pending"},73:{text:"IDoc passed to BTP Integration Suite",category:"pending"},1:{text:"IDoc generated",category:"pending"},3:{text:"IDoc data passed to port OK",category:"success"},5:{text:"IDoc translation error",category:"error"},12:{text:"Dispatch OK",category:"success"},18:{text:"Triggering EDI subsystem OK",category:"success"},30:{text:"IDoc ready to be dispatched",category:"pending"}};async function ei(t,e,n){try{let i=(await t.request({method:"get",url:`${e}/IdocStatus('${n}')`})).data.d,o=i.Status,a=Qs[o];return {docnum:i.Docnum,status:o,statusText:a?.text??"Unknown status",category:a?.category??"pending",timestamp:i.Timestamp}}catch(r){let s=r instanceof Error?r.message:String(r);throw new Error(`Failed to query IDoc status: ${s}`)}}var jn={name:z$2.string().describe("Segment name, e.g. E1MARAM"),fields:z$2.record(z$2.string(),z$2.string()).describe("Segment field values")},bd=z$2.object({...jn}),wd=z$2.object({...jn,children:z$2.array(bd).optional().describe("Nested child segments (level 3)")}),vd=z$2.object({...jn,children:z$2.array(wd).optional().describe("Nested child segments")});function ti(t,e,n,r){function s(i,o){return _(i,{serviceUrl:o})}t.registerTool("sap_idoc_send",{title:"Send IDoc to SAP",description:"Send an IDoc to SAP via HTTP/XML. Auto-constructs EDI_DC40 control record. Validates against segment metadata before sending -- blocks on validation failure.",inputSchema:z$2.object({idocType:z$2.string().describe("IDoc type name, e.g. MATMAS05, ORDERS05"),segments:z$2.array(vd).describe("IDoc data segments"),metadataServiceUrl:z$2.string().optional().describe("OData service URL for IDoc metadata validation. If provided, validates before sending."),mesType:z$2.string().optional().describe("Override auto-derived message type (MESTYP)"),sndpor:z$2.string().optional().describe("Override sender port"),sndprn:z$2.string().optional().describe("Override sender partner number"),rcvpor:z$2.string().optional().describe("Override receiver port"),rcvprn:z$2.string().optional().describe("Override receiver partner number")}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:false,openWorldHint:true}},async({idocType:i,segments:o,metadataServiceUrl:a,mesType:d,sndpor:l,sndprn:c,rcvpor:u,rcvprn:p})=>{try{let m;if(a){let g=await Hn(e,a,i);Array.isArray(g)||(m=g);}let f=await n.send({idocType:i,segments:o,metadata:m,partnerOverrides:{mesType:d,sndpor:l,sndprn:c,rcvpor:u,rcvprn:p}});return {content:[{type:"text",text:JSON.stringify(f,null,2)}]}}catch(m){return s(m)}}),t.registerTool("sap_idoc_status",{title:"Query IDoc Status",description:"Query IDoc processing status in SAP. Returns status code with human-readable description (50=received, 53=processed, 51=error).",inputSchema:z$2.object({docnum:z$2.string().describe("IDoc document number"),statusServiceUrl:z$2.string().describe("OData service URL for IDoc status query")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({docnum:i,statusServiceUrl:o})=>{try{let a=await ei(e,o,i);return {content:[{type:"text",text:JSON.stringify(a,null,2)}]}}catch(a){return s(a)}}),t.registerTool("sap_idoc_discover",{title:"Discover IDoc Types",description:"Discover available IDoc types and segment structures from SAP. Returns IDoc type definitions with segment hierarchy and field metadata.",inputSchema:z$2.object({metadataServiceUrl:z$2.string().describe("OData service URL for IDoc metadata"),idocType:z$2.string().optional().describe("Specific IDoc type to get details for. Omit to list all types.")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({metadataServiceUrl:i,idocType:o})=>{try{let a=await Hn(e,i,o);return {content:[{type:"text",text:JSON.stringify(a,null,2)}]}}catch(a){return s(a)}}),t.registerTool("sap_idoc_list_received",{title:"List Received IDocs",description:"List IDocs received via webhook. Returns recently received IDocs from the in-memory store.",inputSchema:z$2.object({}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async()=>{try{let i=r.list().map(o=>({docnum:o.docnum,idocType:o.idocType,mesType:o.mesType,receivedAt:o.receivedAt.toISOString()}));return {content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return s(i)}});}var Td=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/,Ad=/[\u0000-\u001f\u007f]/;function Vn(t){let e={};if(!t)return e;for(let[n,r]of Object.entries(t)){if(r==null)continue;let s=typeof r=="string"?r.trim():String(r);if(s.length!==0){if(!Td.test(n))throw new Error(`"${n}" ist kein zul\xE4ssiger Kopfzeilenname. Erlaubt sind Buchstaben, Ziffern und -_.~ ohne Leerzeichen.`);if(Ad.test(s))throw new Error(`Der Wert der Kopfzeile "${n}" enth\xE4lt einen Zeilenumbruch oder ein Steuerzeichen. Das w\xE4re eine Kopfzeilen-Einschleusung und wird nicht gesendet.`);e[n]=s;}}return e}var Ed=[[/ä/g,"ae"],[/ö/g,"oe"],[/ü/g,"ue"],[/Ä/g,"Ae"],[/Ö/g,"Oe"],[/Ü/g,"Ue"],[/ß/g,"ss"]],Cd=[[/[\u2010-\u2015]/g,"-"],[/[\u2018\u2019\u201a\u201b]/g,"'"],[/[\u201c\u201d\u201e\u201f]/g,'"'],[/…/g,"..."],[/[\u00a0\u2007\u202f\u2009]/g," "]],Rd=/[\u0300-\u036f]/g,Pd=/[^\u0020-\u007e]/g;function ni(t){let e=t.trim();if(e.length===0)throw new Error("Der Dateiname ist leer. SAP braucht ihn als Slug-Kopfzeile.");let n=e;for(let[r,s]of Ed)n=n.replace(r,s);for(let[r,s]of Cd)n=n.replace(r,s);if(n=n.normalize("NFKD").replace(Rd,""),n=n.replace(Pd,"").trim(),n.length===0)throw new Error(`Aus dem Dateinamen "${e}" bleibt nichts \xFCbrig, was durch eine HTTP-Kopfzeile passt. Gib einen Namen mit lateinischen Zeichen an.`);return {slug:n,changed:n!==e}}async function ri(t,e,n){if(n.content.byteLength===0)throw new Error("Der Rumpf ist leer. Ein Anhang ohne Bytes legt in SAP ein leeres Dokument an, das sich nicht mehr \xE4ndern l\xE4sst (der Dienst kennt kein PATCH).");let{slug:r,changed:s}=ni(n.fileName),i=n.contentType.trim();if(i.length===0)throw new Error("Es fehlt der Content-Type der Datei, z. B. application/pdf.");let o={...Vn(n.headers),...Vn({Slug:r,"Content-Type":i,Accept:"application/json"})},a=await t.request({method:"post",url:`${e}/${n.entitySet}`,data:n.content,headers:o}),d=se(a.data),l=d.results.length>0?z(d.results[0]):{},c={status:a.status,slug:r,bytesSent:n.content.byteLength,entity:l};s&&(c.slugAdjustedFrom=n.fileName);let u=a.headers?.location;return u&&(c.location=u),c}async function si(t,e,n){let r=Oe(n.entitySet,n.key),s=`${e}/${r}${n.withoutValuePath?"":"/$value"}`,i=await t.request({method:"get",url:s,responseType:"arraybuffer"}),o={status:i.status,content:kd(i.data),contentType:String(i.headers?.["content-type"]??"application/octet-stream")},a=_d(i.headers?.["content-disposition"]);return a&&(o.fileName=a),o}function kd(t){return Buffer.isBuffer(t)?t:t instanceof ArrayBuffer?Buffer.from(t):ArrayBuffer.isView(t)?Buffer.from(t.buffer,t.byteOffset,t.byteLength):typeof t=="string"?Buffer.from(t,"binary"):Buffer.alloc(0)}function _d(t){if(typeof t!="string")return;let e=/filename\*=UTF-8''([^;]+)/i.exec(t);if(e?.[1])try{return decodeURIComponent(e[1].trim())}catch{}return /filename="?([^";]+)"?/i.exec(t)?.[1]?.trim()}function ii(t,e,n){function r(s){return Y(s,n)?null:_(new Error(`Entity set '${s}' is not exposed. Configure --expose to include it.`))}t.registerTool("sap_media_upload",{title:"Upload to a SAP Media Entity",description:`L\xE4dt eine Datei als Rohbytes in eine SAP-Media-Entity, z. B. einen Anhang in AttachmentContentSet (API_CV_ATTACHMENT_SRV). Der Dateiname geht als Slug-Kopfzeile hinaus, die Bytes in den Rumpf, die Antwort kommt als JSON.
|
|
250
250
|
|
|
251
251
|
**Kopfzeilen:** objektbezogene Angaben wie BusinessObjectTypeName (Kundenauftrag: BUS2032), LinkedSAPObjectKey (zehnstellig mit f\xFChrenden Nullen) und DocumentInfoRecordDocType geh\xF6ren in \`headers\`. Leere Werte werden nicht gesendet.
|
|
252
252
|
|
|
253
253
|
**Achtung:** Der Anhangdienst kennt kein PATCH \u2014 ein zweiter Upload ersetzt nichts, er h\xE4ngt ein zweites Dokument an dasselbe Objekt. Vorher nachsehen, ob schon eines da ist.
|
|
254
254
|
|
|
255
|
-
**Wann nicht:** f\xFCr gew\xF6hnliche JSON-Entit\xE4ten sap_create verwenden.`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set name, e.g., AttachmentContentSet"),contentBase64:z$2.string().describe("Der Dateiinhalt base64-kodiert. Ein data:-Pr\xE4fix wird abgeschnitten."),fileName:z$2.string().describe("Dateiname, geht als Slug-Kopfzeile hinaus"),contentType:z$2.string().describe("Content-Type der Datei, z. B. application/pdf"),headers:z$2.record(z$2.string(),z$2.unknown()).optional().describe('Weitere Kopfzeilen, z. B. { "BusinessObjectTypeName": "BUS2032", "LinkedSAPObjectKey": "0000012345", "DocumentInfoRecordDocType": "PDF" }. Leere Werte werden nicht gesendet.')}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:false,openWorldHint:true}},async({serviceUrl:s,entitySet:i,contentBase64:o,fileName:a,contentType:d,headers:l})=>{let c=r(i);if(c)return c;let u;try{u=
|
|
255
|
+
**Wann nicht:** f\xFCr gew\xF6hnliche JSON-Entit\xE4ten sap_create verwenden.`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set name, e.g., AttachmentContentSet"),contentBase64:z$2.string().describe("Der Dateiinhalt base64-kodiert. Ein data:-Pr\xE4fix wird abgeschnitten."),fileName:z$2.string().describe("Dateiname, geht als Slug-Kopfzeile hinaus"),contentType:z$2.string().describe("Content-Type der Datei, z. B. application/pdf"),headers:z$2.record(z$2.string(),z$2.unknown()).optional().describe('Weitere Kopfzeilen, z. B. { "BusinessObjectTypeName": "BUS2032", "LinkedSAPObjectKey": "0000012345", "DocumentInfoRecordDocType": "PDF" }. Leere Werte werden nicht gesendet.')}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:false,openWorldHint:true}},async({serviceUrl:s,entitySet:i,contentBase64:o,fileName:a,contentType:d,headers:l})=>{let c=r(i);if(c)return c;let u;try{u=Id(o);}catch(p){return _(p,{serviceUrl:s})}try{let p=j(s),m=await ri(e,p,{entitySet:i,content:u,fileName:a,contentType:d,...l?{headers:l}:{}});return {content:[{type:"text",text:JSON.stringify({uploaded:m},null,2)}]}}catch(p){return _(p,{serviceUrl:s})}}),t.registerTool("sap_media_download",{title:"Download from a SAP Media Entity",description:`Holt die Rohbytes einer SAP-Media-Entity \xFCber den $value-Pfad, z. B. den Inhalt eines Anhangs. Die Bytes kommen als resource/blob zur\xFCck, nicht als base64-Zeichenkette im JSON; dazu Dateiname, Content-Type und Gr\xF6\xDFe als Text.
|
|
256
256
|
|
|
257
|
-
**Wann nicht:** f\xFCr die Metadaten eines Anhangs (Name, Gr\xF6\xDFe, Anleger) reicht sap_read auf dieselbe Entit\xE4t.`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set name, e.g., AttachmentContentSet"),key:z$2.record(z$2.string(),z$2.unknown()).describe('Entity key as object, e.g., { DocumentInfoRecordDocNumber: "10000042" }'),withoutValuePath:z$2.boolean().optional().describe("Ohne /$value anfragen. Nur n\xF6tig, wenn der Dienst die Bytes direkt unter dem Entit\xE4tspfad liefert.")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:s,entitySet:i,key:o,withoutValuePath:a})=>{let d=r(i);if(d)return d;try{let l=j(s),c=await
|
|
257
|
+
**Wann nicht:** f\xFCr die Metadaten eines Anhangs (Name, Gr\xF6\xDFe, Anleger) reicht sap_read auf dieselbe Entit\xE4t.`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set name, e.g., AttachmentContentSet"),key:z$2.record(z$2.string(),z$2.unknown()).describe('Entity key as object, e.g., { DocumentInfoRecordDocNumber: "10000042" }'),withoutValuePath:z$2.boolean().optional().describe("Ohne /$value anfragen. Nur n\xF6tig, wenn der Dienst die Bytes direkt unter dem Entit\xE4tspfad liefert.")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:s,entitySet:i,key:o,withoutValuePath:a})=>{let d=r(i);if(d)return d;try{let l=j(s),c=await si(e,l,{entitySet:i,key:o,...a?{withoutValuePath:a}:{}}),u=`sap://${i}/${encodeURIComponent(c.fileName??"content")}`;return {content:[{type:"text",text:JSON.stringify({status:c.status,...c.fileName?{fileName:c.fileName}:{},contentType:c.contentType,bytes:c.content.byteLength},null,2)},{type:"resource",resource:{uri:u,mimeType:c.contentType,blob:c.content.toString("base64")}}]}}catch(l){return _(l,{serviceUrl:s})}});}var Od=/^[A-Za-z0-9+/\r\n=_-]+$/;function Id(t){let e=t.replace(/^data:[^;,]*;base64,/,"").trim();if(e.length===0)throw new Error("contentBase64 ist leer.");if(!Od.test(e))throw new Error("contentBase64 ist keine base64-Kodierung. Erwartet wird der Dateiinhalt base64-kodiert (im n8n-Knoten \xFCbernimmt das das Bin\xE4rfeld), nicht der Klartext oder ein Dateipfad.");let n=Buffer.from(e,"base64");if(n.byteLength===0)throw new Error("contentBase64 ergibt keine Bytes.");return n}var Nd={core:"Core tools (connection, discovery, metadata, NL query)",odata:"OData CRUD tools (read, query, create, update, delete, function, batch)",idoc:"IDoc tools (send, status, discover, list received)",rfc:"RFC/BAPI tools (rfc_call, rfc_metadata, bapi_call, bapi_commit, rfc_search_functions). Requires SAP_RFC_ASHOST/SYSNR or load-balanced equivalent."};function ai(t,e){if(e.isReadOnlyMode())return;let n=e.getActiveTiers(),r=["core","odata","idoc","rfc"],s=e.getAllowedTiers(),i=r.filter(a=>!n.includes(a)&&(!s||s.includes(a))),o=i.length>0?`Available tiers to activate: ${i.map(a=>`${a} (${Nd[a]})`).join(", ")}.`:"All tiers are already active.";t.registerTool("sap_enable_tools",{title:"Enable Additional SAP Tools",description:`Activate additional tool tiers for this session. Currently active: ${n.join(", ")}. ${o}`,inputSchema:z$2.object({tier:z$2.enum(["core","odata","idoc","rfc"]).describe('Tier to activate. Use "idoc" for IDoc tools, "rfc" for RFC/BAPI tools.')}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},async({tier:a})=>{let d=e.enableTier(a);return "error"in d?{content:[{type:"text",text:JSON.stringify({status:"error",message:d.error})}],isError:true}:{content:[{type:"text",text:JSON.stringify({status:"success",tier_enabled:a,enabled_tools:d.enabled,active_tiers:d.activeTiers,note:"Tools enabled. If your client did not refresh the tool list automatically, you may need to reconnect."})}]}});}function ci(t,e){return t.length===0?"":t.map(n=>Dd(n,e)).join(" and ")}function Dd(t,e){let{field:n,operator:r,value:s}=t;switch(r){case "contains":return xd(n,String(s),e);case "startswith":return `startswith(${n},'${Vt(String(s))}')`;case "endswith":return `endswith(${n},'${Vt(String(s))}')`;default:return Md(n,r,s,e)}}function xd(t,e,n){let r=Vt(e);return n==="v2"?`substringof('${r}',${t})`:`contains(${t},'${r}')`}function Md(t,e,n,r){let s=Ud(n,r);return `${t} ${e} ${s}`}function Ud(t,e){return typeof t=="number"||typeof t=="boolean"?String(t):$d(t)?Ld(t,e):`'${Vt(t)}'`}function $d(t){return /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?)?/.test(t)}function Ld(t,e){return e==="v2"?`datetime'${t.includes("T")?t:`${t}T00:00:00`}'`:t}function Vt(t){return t.replace(/'/g,"''")}function di(t,e,n,r){let s=r??new I,i=new Map;function o(d){let l=j(d),c=i.get(l);return c||(c=new $(e,l,s),i.set(l,c)),c}function a(d,l){return _(d,{serviceUrl:l})}t.registerTool("sap_nl_query",{title:"Execute NL-Generated OData Query",description:`Execute a structured OData query generated from natural language. Accepts structured filters (field/operator/value) and converts them to the correct OData V2 or V4 filter syntax automatically. Read-only: only performs queries, never creates, updates, or deletes data.
|
|
258
258
|
|
|
259
259
|
**When to use:**
|
|
260
260
|
- You have a natural language question about SAP data
|
|
@@ -264,7 +264,7 @@ Fiori draft services (V2, e.g. purchase requisitions): activate a draft with the
|
|
|
264
264
|
**When NOT to use:**
|
|
265
265
|
- You need to create, update, or delete data (use sap_create/sap_update/sap_delete)
|
|
266
266
|
- You already have an exact OData $filter string (use sap_query instead)
|
|
267
|
-
- You don't know which service to query (use sap://routing/{domain} first)`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set to query"),filters:z$2.array(z$2.object({field:z$2.string().describe("Property name from entity schema"),operator:z$2.enum(["eq","ne","gt","ge","lt","le","contains","startswith","endswith"]).describe("Filter operator"),value:z$2.union([z$2.string(),z$2.number(),z$2.boolean()]).describe("Filter value")})).describe("Structured filters from NL interpretation"),select:z$2.array(z$2.string()).optional().describe("Properties to return"),orderby:z$2.string().optional().describe("Sort expression"),top:z$2.number().optional().describe("Maximum number of results"),skip:z$2.number().optional().describe("Number of results to skip")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:d,entitySet:l,filters:c,select:u,orderby:p,top:m,skip:f})=>{if(!
|
|
267
|
+
- You don't know which service to query (use sap://routing/{domain} first)`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set to query"),filters:z$2.array(z$2.object({field:z$2.string().describe("Property name from entity schema"),operator:z$2.enum(["eq","ne","gt","ge","lt","le","contains","startswith","endswith"]).describe("Filter operator"),value:z$2.union([z$2.string(),z$2.number(),z$2.boolean()]).describe("Filter value")})).describe("Structured filters from NL interpretation"),select:z$2.array(z$2.string()).optional().describe("Properties to return"),orderby:z$2.string().optional().describe("Sort expression"),top:z$2.number().optional().describe("Maximum number of results"),skip:z$2.number().optional().describe("Number of results to skip")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:d,entitySet:l,filters:c,select:u,orderby:p,top:m,skip:f})=>{if(!Y(l,n))return a(new Error(`Entity set '${l}' is not exposed. Configure --expose to include it.`));try{let g=o(d),b=(await g.getMetadata()).version,y=ci(c,b),E=await g.query(l,{filter:y||void 0,select:u,orderby:p,top:m,skip:f});return {content:[{type:"text",text:JSON.stringify({version:b,generatedFilter:y||"(none)",results:E.results,count:E.count,nextLink:E.nextLink},null,2)}]}}catch(g){return a(g)}});}function li(t,e,n,r){let s=r??new I,i=new Map;function o(l){let c=j(l),u=i.get(c);return u||(u=new $(e,c,s),i.set(c,u)),u}function a(l,c){return _(l,{serviceUrl:c})}function d(l){return Y(l,n)?null:a(new Error(`Entity set '${l}' is not exposed. Configure --expose to include it.`))}t.registerTool("sap_read",{title:"Read SAP Entity",description:`Read a single entity or entity collection from SAP. Use key parameter for single entity, omit for collection. Supports $select and $expand.
|
|
268
268
|
|
|
269
269
|
**When to use:**
|
|
270
270
|
- You have a specific entity key and want one record
|
|
@@ -282,7 +282,7 @@ Fiori draft services (V2, e.g. purchase requisitions): activate a draft with the
|
|
|
282
282
|
**When NOT to use:**
|
|
283
283
|
- You have a natural language question (use sap_nl_query instead)
|
|
284
284
|
- You don't know which service to query (use sap://routing/{domain} first, then sap_discover_services)
|
|
285
|
-
- You need to create, update, or delete data (use sap_create/sap_update/sap_delete)`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set name"),filter:z$2.string().optional().describe(`OData $filter expression, e.g., "Country eq 'DE'"`),select:z$2.array(z$2.string()).optional().describe("Properties to return"),expand:z$2.array(z$2.string()).optional().describe("Navigation properties to expand"),orderby:z$2.string().optional().describe('Sort expression, e.g., "CreationDate desc"'),top:z$2.number().optional().describe(`Maximum number of results (default ${50}, hard cap ${1e3}). Omitting it does NOT mean "all" \u2014 use skip plus the nextSkip value from the response to page through.`),skip:z$2.number().optional().describe("Number of results to skip"),maxPages:z$2.number().optional().describe("Maximum pagination pages to follow (default: 10)")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:l,entitySet:c,filter:u,select:p,expand:m,orderby:f,top:g,skip:h,maxPages:b})=>{let y=d(c);if(y)return y;try{let E=o(l),S=Ie(g,h),
|
|
285
|
+
- You need to create, update, or delete data (use sap_create/sap_update/sap_delete)`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set name"),filter:z$2.string().optional().describe(`OData $filter expression, e.g., "Country eq 'DE'"`),select:z$2.array(z$2.string()).optional().describe("Properties to return"),expand:z$2.array(z$2.string()).optional().describe("Navigation properties to expand"),orderby:z$2.string().optional().describe('Sort expression, e.g., "CreationDate desc"'),top:z$2.number().optional().describe(`Maximum number of results (default ${50}, hard cap ${1e3}). Omitting it does NOT mean "all" \u2014 use skip plus the nextSkip value from the response to page through.`),skip:z$2.number().optional().describe("Number of results to skip"),maxPages:z$2.number().optional().describe("Maximum pagination pages to follow (default: 10)")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:l,entitySet:c,filter:u,select:p,expand:m,orderby:f,top:g,skip:h,maxPages:b})=>{let y=d(c);if(y)return y;try{let E=o(l),S=Ie(g,h),T=await E.query(c,{filter:u,select:p,expand:m,orderby:f,top:S.top,skip:S.skip,maxPages:b}),x=Ne({returned:T.results.length,totalCount:T.count,paging:S,hasMoreHint:T.nextLink?!0:void 0});return {content:[{type:"text",text:JSON.stringify({...T,...x},null,2)}]}}catch(E){return a(E,l)}}),t.registerTool("sap_create",{title:"Create SAP Entity",description:"Create a new entity in SAP. Pass entity data as a JSON string in the data parameter. Use sap_get_metadata with entityType first to discover required fields.",inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set name"),data:z$2.string().describe(`Entity data as JSON string, e.g. '{"BusinessPartnerCategory": "2", "BusinessPartnerName": "Acme Corp", "Country": "DE"}'`)}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:false,openWorldHint:true}},async({serviceUrl:l,entitySet:c,data:u})=>{let p;try{p=JSON.parse(u);}catch{return {content:[{type:"text",text:JSON.stringify({error:"Invalid JSON in data parameter. Must be a valid JSON string.",example:'{"BusinessPartnerCategory": "2", "BusinessPartnerName": "Acme Corp"}'})}],isError:true}}let m=d(c);if(m)return m;try{let g=await o(l).create(c,p);return {content:[{type:"text",text:JSON.stringify({created:g},null,2)}]}}catch(f){return a(f,l)}}),t.registerTool("sap_update",{title:"Update SAP Entity",description:"Update an existing SAP entity. Provide the entity key and the fields to update as JSON strings. ETag handling is automatic.",inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set name"),key:z$2.string().describe(`Entity key as JSON string, e.g. '{"BusinessPartner": "1000001"}'. Draft entities include DraftUUID and IsActiveEntity (boolean).`),data:z$2.string().describe(`Fields to update as JSON string, e.g. '{"BusinessPartnerName": "New Name"}'`)}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:l,entitySet:c,key:u,data:p})=>{let m,f;try{m=JSON.parse(u),f=JSON.parse(p);}catch{return a(new Error("Invalid JSON in key or data parameter"))}let g=d(c);if(g)return g;try{return await o(l).update(c,m,f),{content:[{type:"text",text:JSON.stringify({updated:!0,entitySet:c,key:u})}]}}catch(h){return a(h,l)}}),t.registerTool("sap_delete",{title:"Delete SAP Entity",description:"Delete an entity from SAP. Provide the entity key. ETag handling is automatic.",inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set name"),key:z$2.string().describe(`Entity key as JSON string, e.g. '{"BusinessPartner": "1000001"}'. Draft entities include DraftUUID and IsActiveEntity (boolean).`)}),annotations:{readOnlyHint:false,destructiveHint:true,idempotentHint:true,openWorldHint:true}},async({serviceUrl:l,entitySet:c,key:u})=>{let p;try{p=JSON.parse(u);}catch{return a(new Error("Invalid JSON in key parameter"))}let m=d(c);if(m)return m;try{return await o(l).delete(c,p),{content:[{type:"text",text:JSON.stringify({deleted:!0,entitySet:c,key:u})}]}}catch(f){return a(f,l)}});}function tt(t){let e=t.RETURN??t.E_RETURN??null;if(e==null)return {messages:[],hasErrors:false,errorCount:0,warningCount:0};let r=(Array.isArray(e)?e:[e]).filter(a=>a!==null&&typeof a=="object").map(a=>Bd(a)),s=r.filter(a=>a.type==="E"||a.type==="A").length,i=r.filter(a=>a.type==="W").length,o=s>0;return {messages:r,hasErrors:o,errorCount:s,warningCount:i}}function Bd(t){let e=String(t.TYPE??"I");return {type:e==="S"||e==="I"||e==="W"||e==="E"||e==="A"?e:"I",id:String(t.ID??""),number:String(t.NUMBER??""),message:String(t.MESSAGE??""),messageV1:Z(t.MESSAGE_V1),messageV2:Z(t.MESSAGE_V2),messageV3:Z(t.MESSAGE_V3),messageV4:Z(t.MESSAGE_V4),parameter:Z(t.PARAMETER),row:Fd(t.ROW),field:Z(t.FIELD),system:Z(t.SYSTEM),logNo:Z(t.LOG_NO),logMsgNo:Z(t.LOG_MSG_NO)}}function Z(t){if(t==null)return;let e=String(t);return e===""?void 0:e}function Fd(t){if(t==null)return;let e=String(t);if(e==="")return;let n=Number.parseInt(e,10);return Number.isNaN(n)?void 0:n}function ui(t){if(!(t instanceof Error))return {code:"UNKNOWN_RFC",message:typeof t=="string"?t:String(t),details:[],rawResponse:t};let e=t;switch(e.code){case "RFC_COMMUNICATION_FAILURE":case "RFC_LOGON_FAILURE":case "RFC_TIMEOUT":case "RFC_INVALID_HANDLE":case "RFC_CLOSED":case "RFC_AUTHORIZATION_FAILURE":case "RFC_AUTHENTICATION_FAILURE":case "RFC_CRYPTOLIB_FAILURE":case "RFC_IO_FAILURE":return {code:e.code,message:e.message,details:[],rawResponse:t};case "RFC_ABAP_MESSAGE":{let n={code:`${e.msgClass??"UNKNOWN"}-${e.msgNumber??"000"}`,message:e.message,severity:e.msgType==="E"||e.msgType==="A"||e.msgType==="X"?"error":e.msgType==="W"?"warning":"info"};return {code:`ABAP_MESSAGE:${e.msgClass??"UNKNOWN"}:${e.msgNumber??"000"}`,message:e.message,details:[n],rawResponse:t}}case "RFC_ABAP_EXCEPTION":return {code:`ABAP_CLASSIC:${e.key??"UNKNOWN"}`,message:e.message,details:[],rawResponse:t};case "RFC_ABAP_CLASS_EXCEPTION":return {code:`ABAP_CLASS:${e.classKey??"UNKNOWN"}`,message:e.message,details:[],rawResponse:t};case "RFC_EXTERNAL_FAILURE":case "RFC_ABAP_RUNTIME_FAILURE":return {code:"RFC_EXTERNAL_RUNTIME",message:e.message,details:[],rawResponse:t};default:return {code:"UNKNOWN_RFC",message:e.message,details:[],rawResponse:t}}}var Wt=null;async function pi(t){if(Wt)return true;try{return Wt=await import('node-rfc'),!0}catch(e){let n=e instanceof Error?e.message:String(e);return t.warn({err:n},"node-rfc not available \u2014 RFC/BAPI tools disabled"),t.warn("Install SAP NW RFC SDK 7.50+ from SAP Support Portal (https://support.sap.com/connectors)"),t.warn("Then run `npm install node-rfc` to enable RFC/BAPI tools"),false}}function mi(){if(!Wt)throw new Error("node-rfc not loaded \u2014 call tryLoadRfc() first");return Wt}var fi=6e4,jd=5e3,Vd=2,Wd=10,xe=class extends Error{constructor(n){super(`Session ${n} expired or not found. Re-run sap_bapi_call to start a new session.`);this.sessionId=n;this.name="SessionNotFoundError";}},Gt=class{pool;sessions=new Map;logger;closed=false;constructor(e,n){let s=mi().Pool;this.pool=new s({connectionParameters:{...e},poolOptions:{low:Vd,high:Wd}}),this.logger=n;}async callOnce(e,n){if(this.closed)throw new Error("RfcConnectionPool closed");let r=await this.pool.acquire();try{return await r.call(e,n)}finally{try{await this.pool.release(r);}catch(s){this.logger.warn({err:s},"rfc-pool: release after callOnce failed");}}}async callBapi(e,n,r){if(this.closed)throw new Error("RfcConnectionPool closed");let s;if(r){let o=this.sessions.get(r);if(!o)throw new xe(r);this.refreshIdle(o),s=o;}else {let o=await this.pool.acquire();s=this.createSession(o);}return {result:await s.client.call(e,n),sessionId:s.id}}async commit(e,n){let r=this.sessions.get(e);if(!r)throw new xe(e);clearTimeout(r.idleTimer);try{let s=n?{WAIT:"X"}:{},i=await r.client.call("BAPI_TRANSACTION_COMMIT",s),{hasErrors:o}=tt(i);return {result:i,committed:!o,sessionId:e}}finally{this.sessions.delete(e);try{await this.pool.release(r.client);}catch(s){this.logger.warn({err:s},"rfc-pool: release after commit failed");}}}async closeAll(){if(this.closed)return;this.closed=true;let e=Date.now()+jd;for(;this.sessions.size>0&&Date.now()<e;)await new Promise(n=>setTimeout(n,100));for(let n of this.sessions.values()){clearTimeout(n.idleTimer);try{await this.pool.release(n.client);}catch(r){this.logger.warn({err:r},"rfc-pool: force-release on closeAll failed");}}this.sessions.clear(),await this.pool.closeAll();}sessionCount(){return this.sessions.size}createSession(e){let n=randomUUID(),r={id:n,client:e,createdAt:new Date,idleTimer:setTimeout(()=>this.onIdle(n),fi)};return this.sessions.set(n,r),r}refreshIdle(e){clearTimeout(e.idleTimer),e.idleTimer=setTimeout(()=>this.onIdle(e.id),fi);}async onIdle(e){let n=this.sessions.get(e);if(n){this.sessions.delete(e);try{await this.pool.release(n.client);}catch(r){this.logger.warn({err:r,sessionId:e},"rfc-pool: release on idle-timeout failed");}}}};function nt(t){if(t instanceof xe)return {content:[{type:"text",text:JSON.stringify({code:"SESSION_NOT_FOUND",message:t.message,sessionId:t.sessionId})}],isError:true};let e=ui(t);return {content:[{type:"text",text:JSON.stringify(e)}],isError:true}}var Gd={I:"IMPORT",E:"EXPORT",C:"CHANGING",T:"TABLES",X:"EXCEPTION"};function gi(t,e,n){t.registerTool("sap_rfc_call",{title:"Call SAP RFC Function Module",description:`Invoke any RFC-enabled function module on the SAP backend.
|
|
286
286
|
|
|
287
287
|
When to use:
|
|
288
288
|
- Custom Z_* function modules (no BAPI commit needed)
|
|
@@ -301,7 +301,7 @@ When to use:
|
|
|
301
301
|
When NOT to use:
|
|
302
302
|
- To search for function modules by name pattern -> use sap_rfc_search_functions
|
|
303
303
|
|
|
304
|
-
Returns a structured FunctionInterface object: name, remoteEnabled, updateTask, parameters[].`,inputSchema:z$2.object({functionModule:z$2.string().min(1).describe("Function module name, e.g. BAPI_USER_GET_DETAIL"),language:z$2.string().length(1).optional().describe("Single-character language key for parameter texts (default: server config)")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({functionModule:r,language:s})=>{try{let i=r.toUpperCase(),o={FUNCNAME:i};s&&(o.LANGUAGE=s);let a=await e.callOnce("RFC_GET_FUNCTION_INTERFACE",o),l=(a.PARAMS??[]).map(u=>{let p=String(u.PARAMCLASS??"").trim(),m=
|
|
304
|
+
Returns a structured FunctionInterface object: name, remoteEnabled, updateTask, parameters[].`,inputSchema:z$2.object({functionModule:z$2.string().min(1).describe("Function module name, e.g. BAPI_USER_GET_DETAIL"),language:z$2.string().length(1).optional().describe("Single-character language key for parameter texts (default: server config)")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({functionModule:r,language:s})=>{try{let i=r.toUpperCase(),o={FUNCNAME:i};s&&(o.LANGUAGE=s);let a=await e.callOnce("RFC_GET_FUNCTION_INTERFACE",o),l=(a.PARAMS??[]).map(u=>{let p=String(u.PARAMCLASS??"").trim(),m=Gd[p]??"IMPORT",f=String(u.TABNAME??"").trim(),g=String(u.FIELDNAME??"").trim(),h=f&&g?`${f}-${g}`:f||g||void 0,b=String(u.DECIMALS??"").trim(),y=b===""?void 0:Number.parseInt(b,10),E=Number.isNaN(y)?void 0:y,S=String(u.INTLENGTH??"0").trim(),T=Number.parseInt(S===""?"0":S,10)||0,x=String(u.DEFAULT??"").trim(),G=String(u.PARAMTEXT??"").trim();return {name:String(u.PARAMETER??"").trim(),direction:m,type:String(u.EXID??"").trim(),length:T,decimals:E,optional:String(u.OPTIONAL??"").trim()==="X",default:x===""?void 0:x,text:G===""?void 0:G,ddicReference:h}}),c={name:i,remoteEnabled:String(a.REMOTE_CALL??"")==="X",updateTask:String(a.UPDATE_TASK??"")==="X",parameters:l};return {content:[{type:"text",text:JSON.stringify(c,null,2)}]}}catch(i){return nt(i)}}),t.registerTool("sap_bapi_call",{title:"Call SAP BAPI (stateful)",description:`Invoke a BAPI (Business API) on a stateful connection. Returns a sessionId for subsequent BAPI calls and the eventual sap_bapi_commit.
|
|
305
305
|
|
|
306
306
|
When to use:
|
|
307
307
|
- BAPI_USER_CREATE1, BAPI_SALESORDER_CREATEFROMDAT2, BAPI_*_CHANGE, etc.
|
|
@@ -334,7 +334,7 @@ When NOT to use:
|
|
|
334
334
|
Wildcards (SAP-native, NOT glob):
|
|
335
335
|
- \`*\` matches any sequence of characters
|
|
336
336
|
- \`+\` matches a single character
|
|
337
|
-
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 d=(await e.callOnce("RFC_FUNCTION_SEARCH",o)).FUNCTIONS??[],l=d.length,c=i??100,u=l>c,f={functions:(u?d.slice(0,c):d).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 nt(o)}});}function qt(t,e){let n=new McpServer({name:is,version:X},{capabilities:{logging:{},resources:{},prompts:{},tools:{listChanged:true}}}),r={readOnly:e?.readOnly??false,activeTiers:e?.tiers??Rn,...e?.allowedTiers?{allowedTiers:e.allowedTiers}:{},hasIdocConfig:e?.hasIdocConfig??false,hasRfcConfig:e?.hasRfcConfig??false},s=new Ot(r),i=n.registerTool.bind(n),o=e?.toolTimeoutMs??It*1e3;n.registerTool=((h,...b)=>{let y=b.length-1,E=b[y];typeof E=="function"&&(b[y]=cs(h,E,{toolTimeoutMs:o,logger:e?.logger,tlsVerifyDisabled:e?.httpOptions?.tlsVerify===false,...e?.governance?{governance:e.governance,destination:t.name,...e.callerIdentity?{callerIdentity:e.callerIdentity}:{}}:{}}));let S=i(h,...b);return s.registerHandle(h,S),S});let a={baseUrl:t.baseUrl,...e?.cacheDir?{persistDir:e.cacheDir}:{},...e?.logger?{onWarn:(h,b)=>e.logger?.warn({err:b},h)}:{}},d=e?.caches?.catalog??new re(a),l=e?.caches?.metadata??new I(a),c=ot(t),u=new Te(c,e?.httpOptions);e?.userContext&&(u.userContext=e.userContext);let p=e?.exposePatterns,m=e?.sapClient??t.sapClient;ls(n),ds(n),Js(n,u,{baseUrl:t.baseUrl,sapClient:m,tlsVerify:e?.httpOptions?.tlsVerify}),Ks(n,u,p),qs(n,u,{catalogV2Url:e?.catalogV2Url,catalogV4Url:e?.catalogV4Url,baseUrl:t.baseUrl,onDiscoveryMatch:e?.onDiscoveryMatch,...e?.concurrency?{concurrency:e.concurrency}:{}},d),di(n,u,p,l),Xs(n,u,l,e?.readOnly??false),si(n,u,p),Ls(n,u,l),ci(n,u,p,l);let f=e?.idocStore??new _e,g=new kt(u,m,e?.partnerDefaults);return ei(n,u,g,f),Ds(n,f),e?.rfcAvailable&&e.rfcPool&&e.logger&&fi(n,e.rfcPool,e.logger),oi(n,s),xs(n,u,l),Ns(n,u,l),Us(n),s.applyInitialVisibility(),{server:n,registry:s,httpClient:u}}var Vd=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 Wn(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=Vd.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(d=>d.message).join("; ");throw new Error(`Invalid SNC configuration: ${a}`)}return o.data.snc_qop&&Object.assign(i,o.data),i}function Gn(t=process.env){try{return Wn(t)!==null}catch{return false}}var Kt=class{constructor(e={}){this.options=e;this.discovery=e.discoveryClient??new de;}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 Kd(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 Kd(t){let e=t.split(".");return e.length===3&&e.every(n=>n.length>0)}var zd="x-sap-username",Yd="x-sap-password",Xd="x-sap-authorization";function qn(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 Jd(t){let e=qn(t,zd),n=qn(t,Yd);if(e&&n)return {username:e,password:n};let r=qn(t,Xd),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 gi(t,e){let n=Jd(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 Zd=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 he(t,e,n){t.setHeader("Cache-Control","no-store"),t.status(e).json(n);}function hi(t){return t.ip??t.socket?.remoteAddress??"unknown"}function yi(t,e){let n=t?.[e];return n==="true"||n==="1"||n===""}function Si(t){t.use("/admin",(e,n)=>{he(n,404,{error:"Admin API not enabled",hint:"Start the server with --admin-token <secret> (or SAP_MCP_ADMIN_TOKEN) and --destinations <file>."});});}function bi(t,e){let{destinations:n,logger:r}=e,s=new Ce({apiKey:e.adminToken,tokens:()=>[]});t.use("/admin",(d,l,c)=>{let u=d,p=l,m=hi(u),f=s.authenticate(u.headers.authorization,m);if(f.kind==="rate-limited"){r?.warn({clientAddress:m,path:u.originalUrl??u.path,retryAfterSeconds:f.retryAfterSeconds},"Admin-API: zu viele fehlgeschlagene Anmeldeversuche von dieser Adresse"),p.setHeader("Retry-After",String(f.retryAfterSeconds)),he(p,429,{error:"Too Many Requests",retryAfterSeconds:f.retryAfterSeconds});return}if(f.kind!=="api-key"){r?.warn({clientAddress:m,path:u.originalUrl??u.path},"Admin-API: Anfrage abgewiesen"),he(p,401,{error:"Unauthorized"});return}c();});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 Re({filePath:n.filePath,env:e.env})},o=(d,l)=>(c,u)=>{let p=c,m=u,f=hi(p);(async()=>{try{let{status:g,body:h,changed:b}=l(p);if(b){let y=await n.reload();h.reloaded=y.ok,y.ok||(h.reloadErrors=y.errors),r?.info({action:d,clientAddress:f,target:p.params?.name??p.params?.ref??p.body?.destination,reloaded:y.ok},"Admin-API: \xC4nderung ausgef\xFChrt");let E=p.params?.name??p.params?.ref??p.body?.destination;try{e.governance?.onAdminAction?.({apiVersion:1,serverVersion:X,at:new Date().toISOString(),action:d,...typeof E=="string"?{target:E}:{},...f?{clientAddress:f}:{},reloaded:y.ok});}catch(S){r?.warn({err:S,action:d},"Governance-Modul: Admin-Aktion konnte nicht gemeldet werden");}}he(m,g,h);}catch(g){if(g instanceof P){he(m,g.status,{error:g.message});return}r?.warn({action:d,clientAddress:f,err:g instanceof Error?g.message:String(g)},"Admin-API: unerwarteter Fehler"),he(m,500,{error:"Internal Server Error"});}})();},a=d=>{let l=d.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",d=>{let l=d.params?.name??"",c=a(d),{replaced:u,warnings:p}=i().addDestination(l,c,{replace:!0,allowMissingEnv:yi(d.query,"allowMissingEnv")});return {status:u?200:201,body:{name:l,replaced:u,warnings:p},changed:!0}})),t.delete("/admin/destinations/:name",o("destinations.delete",d=>{let l=d.params?.name??"",{removedTokens:c}=i().removeDestination(l,{force:yi(d.query,"force")});return {status:200,body:{name:l,removedTokens:c},changed:!0}})),t.get("/admin/tokens",o("tokens.list",()=>({status:200,body:{tokens:i().listTokens()},changed:!1}))),t.post("/admin/tokens",o("tokens.issue",d=>{let l=Zd.safeParse(a(d));if(!l.success)throw new P(`Invalid body: ${l.error.issues.map(g=>`${g.path.join(".")||"(root)"}: ${g.message}`).join("; ")}`,400);let{destination:c,...u}=l.data,{token:p,hash:m,entry:f}=i().issueToken(c,u);return {status:201,body:{token:p,hash:m.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",d=>{let l=d.params?.ref??"",c=i().revokeToken(l);return {status:200,body:{...c,revoked:!0},changed:!c.alreadyRevoked}})),t.use("/admin",(d,l)=>{he(l,404,{error:"Not Found"});});}var tl=16*1024*1024;function nl(t){let e=wi();return e.use(wi.json({limit:t})),e}function rl(t,e){return e?.kind==="token"?t.destinations?.get(e.token.destination):t.destinations?t.destinations.defaultDestination():t.config}function sl(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 il(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 Kn=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 re(i),metadata:new I(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 ol(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 al(t,e,n){let r=t?.kind==="token"?"token":t?.kind==="api-key"?"api-key":"open",s=n?n.email??n.sub:void 0;return {authKind:r,...t?.kind==="token"?{tokenLabel:t.token.label??t.token.hash.slice(7,19)}:{},...e?{sapUser:e}:{},...s?{subject:s,subjectVerified:n?.verified===true}:{},personal:!!(e||s)}}function cl(t,e){let n=t.logger;return !e||!n||typeof n.child!="function"?t:{...t,logger:n.child({sapUser:e})}}async function Ti(t){let e=new Kt({...t.jwtIssuer?{issuerUrl:t.jwtIssuer}:{},...t.jwtAudience?{audience:t.jwtAudience}:{}}),n=t.maxRequestBytes??tl,r=nl(n),s=new Ce({apiKey:t.apiKey,tokens:()=>t.destinations?.tokens()??[]}),i=t.serverOptions?.logger;r.use((d,l,c)=>{let u=d,p=l,m=c;if(u.path==="/healthz"){m();return}if(u.path==="/admin"||u.path.startsWith("/admin/")){m();return}if(t.webhookSecret&&u.path==="/idoc"){m();return}let f=u.ip??u.socket?.remoteAddress??"unknown",g=s.authenticate(u.headers.authorization,f);if(g.kind==="rate-limited"){i?.warn({clientAddress:f,path:u.path,retryAfterSeconds:g.retryAfterSeconds},"MCP-Anfrage abgewiesen: zu viele fehlgeschlagene Anmeldeversuche von dieser Adresse"),p.setHeader("Retry-After",String(g.retryAfterSeconds)),p.status(429).json({error:"Too Many Requests",retryAfterSeconds:g.retryAfterSeconds});return}if(g.kind==="rejected"){i?.warn({clientAddress:f,path:u.path,reason:g.reason},"MCP-Anfrage abgewiesen"),p.status(401).json({error:"Unauthorized"});return}u.sapAuth=g,m();}),r.get("/healthz",(d,l)=>{l.status(200).json({status:"ok",governance:t.governanceHealth?.()??{active:false}});}),t.adminToken&&t.destinations?bi(r,{adminToken:t.adminToken,destinations:t.destinations,env:t.env??process.env,logger:i,...t.serverOptions.governance?{governance:t.serverOptions.governance}:{}}):Si(r),dl(r,t,e);let o=Ct(t.idocStore,{secret:t.webhookSecret,maxBodyBytes:t.webhookMaxBodyBytes});r.post("/idoc",(d,l)=>{o(d,l);}),r.use((d,l,c,u)=>{let p=d,m=c;if(p?.type==="entity.parse.failed"&&!m.headersSent){m.status(400).json({error:`Invalid JSON body: ${p.message??"parse error"}`});return}if(p?.type==="entity.too.large"&&!m.headersSent){let f=(n/1024/1024).toFixed(1);m.status(413).json({error:`Anfrage zu gro\xDF: die Grenze liegt bei ${n} Bytes (${f} MiB). Bei einem base64-kodierten Anhang sind das rund ${(n*3/4/1024/1024).toFixed(1)} MiB Datei. Mit --max-request-bytes anheben.`});return}u(d);});let a=createServer(r);return new Promise(d=>{a.listen(t.port,()=>{let l=a.address(),c=typeof l=="object"&&l!==null?l.port:t.port;d({port:c,close:()=>new Promise((u,p)=>{a.close(m=>m?p(m):u());})});});})}function dl(t,e,n){let r=new Kn(e.serverOptions?.cacheDir),s=e.serverOptions?.logger;t.all("/mcp",(i,o)=>{let a=i,d=o,l=rl(e,a.sapAuth);if(!l){il(d,e.destinations);return}let c=gi(l,a.headers);if(c.kind!=="ok"){s?.warn({destination:l.name,clientAddress:a.socket?.remoteAddress,reason:c.kind},c.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"),ol(d,c);return}let u=c.config,p=c.username,m=new StreamableHTTPServerTransport({sessionIdGenerator:void 0}),f=g=>{let{server:h}=qt(u,{...sl(cl(e.serverOptions,p),a.sapAuth),sapClient:u.sapClient,userContext:g,caches:r.for(u,p),callerIdentity:al(a.sapAuth,p,g)});h.connect(m).then(()=>m.handleRequest(a,d,a.body)).catch(()=>{d.headersSent||(d.writeHead(500),d.end("Internal Server Error"));});};n.verify(a.headers).then(g=>{if(g.kind==="rejected"){d.writeHead(401,{"Content-Type":"application/json"}),d.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);});});}var ml=`Usage: guniweb-sap-mcp [options]
|
|
337
|
+
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 d=(await e.callOnce("RFC_FUNCTION_SEARCH",o)).FUNCTIONS??[],l=d.length,c=i??100,u=l>c,f={functions:(u?d.slice(0,c):d).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 nt(o)}});}function qt(t,e){let n=new McpServer({name:os,version:K},{capabilities:{logging:{},resources:{},prompts:{},tools:{listChanged:true}}}),r={readOnly:e?.readOnly??false,activeTiers:e?.tiers??Rn,...e?.allowedTiers?{allowedTiers:e.allowedTiers}:{},hasIdocConfig:e?.hasIdocConfig??false,hasRfcConfig:e?.hasRfcConfig??false},s=new Ot(r),i=n.registerTool.bind(n),o=e?.toolTimeoutMs??It*1e3;n.registerTool=((h,...b)=>{let y=b.length-1,E=b[y];typeof E=="function"&&(b[y]=ds(h,E,{toolTimeoutMs:o,logger:e?.logger,tlsVerifyDisabled:e?.httpOptions?.tlsVerify===false,...e?.governance?{governance:e.governance,destination:t.name,...e.callerIdentity?{callerIdentity:e.callerIdentity}:{}}:{}}));let S=i(h,...b);return s.registerHandle(h,S),S});let a={baseUrl:t.baseUrl,...e?.cacheDir?{persistDir:e.cacheDir}:{},...e?.logger?{onWarn:(h,b)=>e.logger?.warn({err:b},h)}:{}},d=e?.caches?.catalog??new re(a),l=e?.caches?.metadata??new I(a),c=ot(t),u=new ve(c,e?.httpOptions);e?.userContext&&(u.userContext=e.userContext);let p=e?.exposePatterns,m=e?.sapClient??t.sapClient;us(n),ls(n),Zs(n,u,{baseUrl:t.baseUrl,sapClient:m,tlsVerify:e?.httpOptions?.tlsVerify}),zs(n,u,p),Ks(n,u,{catalogV2Url:e?.catalogV2Url,catalogV4Url:e?.catalogV4Url,baseUrl:t.baseUrl,onDiscoveryMatch:e?.onDiscoveryMatch,...e?.concurrency?{concurrency:e.concurrency}:{}},d),li(n,u,p,l),Js(n,u,l,e?.readOnly??false),ii(n,u,p),Bs(n,u,l),di(n,u,p,l);let f=e?.idocStore??new _e,g=new kt(u,m,e?.partnerDefaults);return ti(n,u,g,f),xs(n,f),e?.rfcAvailable&&e.rfcPool&&e.logger&&gi(n,e.rfcPool,e.logger),ai(n,s),Ms(n,u,l),Ds(n,u,l),$s(n),s.applyInitialVisibility(),{server:n,registry:s,httpClient:u}}var Kd=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 Wn(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=Kd.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(d=>d.message).join("; ");throw new Error(`Invalid SNC configuration: ${a}`)}return o.data.snc_qop&&Object.assign(i,o.data),i}function Gn(t=process.env){try{return Wn(t)!==null}catch{return false}}var Kt=class{constructor(e={}){this.options=e;this.discovery=e.discoveryClient??new de;}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 Jd(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 Jd(t){let e=t.split(".");return e.length===3&&e.every(n=>n.length>0)}var Zd="x-sap-username",Qd="x-sap-password",el="x-sap-authorization";function qn(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 tl(t){let e=qn(t,Zd),n=qn(t,Qd);if(e&&n)return {username:e,password:n};let r=qn(t,el),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 hi(t,e){let n=tl(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 nl=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 he(t,e,n){t.setHeader("Cache-Control","no-store"),t.status(e).json(n);}function yi(t){return t.ip??t.socket?.remoteAddress??"unknown"}function Si(t,e){let n=t?.[e];return n==="true"||n==="1"||n===""}function bi(t){t.use("/admin",(e,n)=>{he(n,404,{error:"Admin API not enabled",hint:"Start the server with --admin-token <secret> (or SAP_MCP_ADMIN_TOKEN) and --destinations <file>."});});}function wi(t,e){let{destinations:n,logger:r}=e,s=new Ce({apiKey:e.adminToken,tokens:()=>[]});t.use("/admin",(d,l,c)=>{let u=d,p=l,m=yi(u),f=s.authenticate(u.headers.authorization,m);if(f.kind==="rate-limited"){r?.warn({clientAddress:m,path:u.originalUrl??u.path,retryAfterSeconds:f.retryAfterSeconds},"Admin-API: zu viele fehlgeschlagene Anmeldeversuche von dieser Adresse"),p.setHeader("Retry-After",String(f.retryAfterSeconds)),he(p,429,{error:"Too Many Requests",retryAfterSeconds:f.retryAfterSeconds});return}if(f.kind!=="api-key"){r?.warn({clientAddress:m,path:u.originalUrl??u.path},"Admin-API: Anfrage abgewiesen"),he(p,401,{error:"Unauthorized"});return}c();});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 Re({filePath:n.filePath,env:e.env})},o=(d,l)=>(c,u)=>{let p=c,m=u,f=yi(p);(async()=>{try{let{status:g,body:h,changed:b}=l(p);if(b){let y=await n.reload();h.reloaded=y.ok,y.ok||(h.reloadErrors=y.errors),r?.info({action:d,clientAddress:f,target:p.params?.name??p.params?.ref??p.body?.destination,reloaded:y.ok},"Admin-API: \xC4nderung ausgef\xFChrt");let E=p.params?.name??p.params?.ref??p.body?.destination;try{e.governance?.onAdminAction?.({apiVersion:1,serverVersion:K,at:new Date().toISOString(),action:d,...typeof E=="string"?{target:E}:{},...f?{clientAddress:f}:{},reloaded:y.ok});}catch(S){r?.warn({err:S,action:d},"Governance-Modul: Admin-Aktion konnte nicht gemeldet werden");}}he(m,g,h);}catch(g){if(g instanceof P){he(m,g.status,{error:g.message});return}r?.warn({action:d,clientAddress:f,err:g instanceof Error?g.message:String(g)},"Admin-API: unerwarteter Fehler"),he(m,500,{error:"Internal Server Error"});}})();},a=d=>{let l=d.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",d=>{let l=d.params?.name??"",c=a(d),{replaced:u,warnings:p}=i().addDestination(l,c,{replace:!0,allowMissingEnv:Si(d.query,"allowMissingEnv")});return {status:u?200:201,body:{name:l,replaced:u,warnings:p},changed:!0}})),t.delete("/admin/destinations/:name",o("destinations.delete",d=>{let l=d.params?.name??"",{removedTokens:c}=i().removeDestination(l,{force:Si(d.query,"force")});return {status:200,body:{name:l,removedTokens:c},changed:!0}})),t.get("/admin/tokens",o("tokens.list",()=>({status:200,body:{tokens:i().listTokens()},changed:!1}))),t.post("/admin/tokens",o("tokens.issue",d=>{let l=nl.safeParse(a(d));if(!l.success)throw new P(`Invalid body: ${l.error.issues.map(g=>`${g.path.join(".")||"(root)"}: ${g.message}`).join("; ")}`,400);let{destination:c,...u}=l.data,{token:p,hash:m,entry:f}=i().issueToken(c,u);return {status:201,body:{token:p,hash:m.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",d=>{let l=d.params?.ref??"",c=i().revokeToken(l);return {status:200,body:{...c,revoked:!0},changed:!c.alreadyRevoked}})),t.use("/admin",(d,l)=>{he(l,404,{error:"Not Found"});});}var il=16*1024*1024;function ol(t){let e=vi();return e.use(vi.json({limit:t})),e}function al(t,e){return e?.kind==="token"?t.destinations?.get(e.token.destination):t.destinations?t.destinations.defaultDestination():t.config}function cl(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 dl(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 Kn=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 re(i),metadata:new I(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 ll(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 ul(t,e,n){let r=t?.kind==="token"?"token":t?.kind==="api-key"?"api-key":"open",s=n?n.email??n.sub:void 0;return {authKind:r,...t?.kind==="token"?{tokenLabel:t.token.label??t.token.hash.slice(7,19)}:{},...e?{sapUser:e}:{},...s?{subject:s,subjectVerified:n?.verified===true}:{},personal:!!(e||s)}}function pl(t,e){let n=t.logger;return !e||!n||typeof n.child!="function"?t:{...t,logger:n.child({sapUser:e})}}async function Ti(t){let e=new Kt({...t.jwtIssuer?{issuerUrl:t.jwtIssuer}:{},...t.jwtAudience?{audience:t.jwtAudience}:{}}),n=t.maxRequestBytes??il,r=ol(n),s=new Ce({apiKey:t.apiKey,tokens:()=>t.destinations?.tokens()??[]}),i=t.serverOptions?.logger;r.use((d,l,c)=>{let u=d,p=l,m=c;if(u.path==="/healthz"){m();return}if(u.path==="/admin"||u.path.startsWith("/admin/")){m();return}if(t.webhookSecret&&u.path==="/idoc"){m();return}let f=u.ip??u.socket?.remoteAddress??"unknown",g=s.authenticate(u.headers.authorization,f);if(g.kind==="rate-limited"){i?.warn({clientAddress:f,path:u.path,retryAfterSeconds:g.retryAfterSeconds},"MCP-Anfrage abgewiesen: zu viele fehlgeschlagene Anmeldeversuche von dieser Adresse"),p.setHeader("Retry-After",String(g.retryAfterSeconds)),p.status(429).json({error:"Too Many Requests",retryAfterSeconds:g.retryAfterSeconds});return}if(g.kind==="rejected"){i?.warn({clientAddress:f,path:u.path,reason:g.reason},"MCP-Anfrage abgewiesen"),p.status(401).json({error:"Unauthorized"});return}u.sapAuth=g,m();}),r.get("/healthz",(d,l)=>{l.status(200).json({status:"ok",governance:t.governanceHealth?.()??{active:false}});}),t.adminToken&&t.destinations?wi(r,{adminToken:t.adminToken,destinations:t.destinations,env:t.env??process.env,logger:i,...t.serverOptions.governance?{governance:t.serverOptions.governance}:{}}):bi(r),ml(r,t,e);let o=Ct(t.idocStore,{secret:t.webhookSecret,maxBodyBytes:t.webhookMaxBodyBytes});r.post("/idoc",(d,l)=>{o(d,l);}),r.use((d,l,c,u)=>{let p=d,m=c;if(p?.type==="entity.parse.failed"&&!m.headersSent){m.status(400).json({error:`Invalid JSON body: ${p.message??"parse error"}`});return}if(p?.type==="entity.too.large"&&!m.headersSent){let f=(n/1024/1024).toFixed(1);m.status(413).json({error:`Anfrage zu gro\xDF: die Grenze liegt bei ${n} Bytes (${f} MiB). Bei einem base64-kodierten Anhang sind das rund ${(n*3/4/1024/1024).toFixed(1)} MiB Datei. Mit --max-request-bytes anheben.`});return}u(d);});let a=createServer(r);return new Promise(d=>{a.listen(t.port,()=>{let l=a.address(),c=typeof l=="object"&&l!==null?l.port:t.port;d({port:c,close:()=>new Promise((u,p)=>{a.close(m=>m?p(m):u());})});});})}function ml(t,e,n){let r=new Kn(e.serverOptions?.cacheDir),s=e.serverOptions?.logger;t.all("/mcp",(i,o)=>{let a=i,d=o,l=al(e,a.sapAuth);if(!l){dl(d,e.destinations);return}let c=hi(l,a.headers);if(c.kind!=="ok"){s?.warn({destination:l.name,clientAddress:a.socket?.remoteAddress,reason:c.kind},c.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"),ll(d,c);return}let u=c.config,p=c.username,m=new StreamableHTTPServerTransport({sessionIdGenerator:void 0}),f=g=>{let{server:h}=qt(u,{...cl(pl(e.serverOptions,p),a.sapAuth),sapClient:u.sapClient,userContext:g,caches:r.for(u,p),callerIdentity:ul(a.sapAuth,p,g)});h.connect(m).then(()=>m.handleRequest(a,d,a.body)).catch(()=>{d.headersSent||(d.writeHead(500),d.end("Internal Server Error"));});};n.verify(a.headers).then(g=>{if(g.kind==="rejected"){d.writeHead(401,{"Content-Type":"application/json"}),d.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);});});}var yl=`Usage: guniweb-sap-mcp [options]
|
|
338
338
|
guniweb-sap-mcp destinations list|add|remove ... (manage destinations.json)
|
|
339
339
|
guniweb-sap-mcp tokens issue|revoke|list ... (see: guniweb-sap-mcp tokens --help)
|
|
340
340
|
|
|
@@ -389,6 +389,7 @@ Options:
|
|
|
389
389
|
Also SAP_MCP_ADMIN_TOKEN; min. 16 characters.
|
|
390
390
|
The admin secret opens ONLY /admin, never /mcp.
|
|
391
391
|
--help, -h Show this help and exit
|
|
392
|
+
--version Print the version and exit
|
|
392
393
|
|
|
393
394
|
Configuration via environment variables: SAP_BASE_URL, SAP_AUTH_TYPE
|
|
394
395
|
(basic, user-basic = personal SAP login per request via X-SAP-Username/
|
|
@@ -406,5 +407,6 @@ Configuration via environment variables: SAP_BASE_URL, SAP_AUTH_TYPE
|
|
|
406
407
|
|
|
407
408
|
Documentation, releases, issues: https://github.com/guniweb/guniweb-sap-mcp
|
|
408
409
|
Support: support@guniweb.de \u2014 https://guniweb.de/sap-mcp
|
|
409
|
-
`;async function
|
|
410
|
+
`;async function Sl(){let t=process.argv.slice(2);if(t[0]!==void 0&&Dr.has(t[0])){let S=await xr(t,{env:process.env,stdout:T=>process.stdout.write(T),stderr:T=>process.stderr.write(T)});process.exit(S);}let e=Yn();e.help&&(process.stdout.write(yl),process.exit(0)),e.version&&(process.stdout.write(`${K}
|
|
411
|
+
`),process.exit(0)),Xn(t),Jn(e.transport);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): im stdio-Betrieb gibt es keinen /admin-Endpunkt.");let s;if(e.demo){let S=["SAP_BASE_URL","SAP_AUTH_TYPE"].filter(T=>process.env[T]);if(e.destinationsFile||S.length>0)throw new Error(`--demo und eine echte SAP-Konfiguration schlie\xDFen sich aus (gefunden: ${[...e.destinationsFile?["--destinations/SAP_MCP_DESTINATIONS"]:[],...S].join(", ")}). F\xFCr die Demo alle SAP_*-Variablen und --destinations weglassen.`);s=await Ar();}let i=[],o=s?ke.fromEnv(bl(s.baseUrl)):e.destinationsFile?await ke.fromFile(e.destinationsFile,{env:process.env,onWarn:S=>i.push(S),allowMissing:r}):ke.fromEnv(process.env),a=o.defaultDestination(),d=o.tokens().length;if(!a&&(n==="stdio"||d===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=rn(process.env),c=go({redact:gt,transport:{target:"pino/file",options:{destination:2}}});l.tlsVerify===false&&c.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&&c.info({hasCert:true,hasPassphrase:!!l.clientKeyPassphrase},"X.509 client certificate enabled for OData transport"),c.info(a?{baseUrl:a.baseUrl,authType:a.authType}:{destinations:o.names(),tokens:d},"SAP MCP server starting"),s&&c.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 S of i)c.warn(S);if(r&&!a&&d===0&&c.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"?(c.info({file:o.filePath,destinations:o.names(),defaultDestination:a?.name??null,tokens:d},"Destinations aus Datei geladen"),o.onChange(S=>{c.info({version:S.version,destinations:S.names,tokens:S.tokenCount},"Destinations neu geladen");}),o.onError(S=>{c.warn({errors:S},"Destinations-Datei ung\xFCltig \u2014 die letzte g\xFCltige Konfiguration bleibt in Kraft");})):c.info({destinations:o.names()},'Destination "default" aus SAP_*-Umgebung (keine destinations.json konfiguriert)'),e.allowWriteExplicit){let S=rs({tiers:e.tiers,hasIdocConfig:nn(e),hasRfcConfig:Gn(process.env)});c.warn({writeTools:S},`Schreibwerkzeuge sind aktiv (--allow-write): ${S.join(", ")} k\xF6nnen Daten im SAP-System ver\xE4ndern. sap_function ist nicht darunter: dort entscheiden die Metadaten des Dienstes je Aufruf, ob er lesend ist.`);}else c.info("Nur-Lese-Betrieb (Standard seit 1.0). Schreibwerkzeuge sind nicht registriert; mit --allow-write bzw. SAP_MCP_ALLOW_WRITE=true aktivieren.");for(let S of o.names())dr(o.names().length>1?c.child({destination:S}):c,o.get(S)?.sapClient);e.transport&&e.transport!=="stdio"&&(process.env.SAP_JWT_ISSUER?c.info({issuer:process.env.SAP_JWT_ISSUER},"Eingehende Benutzer-Token werden gegen die JWKS des Issuers gepr\xFCft"):c.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&&c.info({exposePatterns:e.expose},"Entity set filtering active");let u=await pi(c),p;if(u){let S=Wn(process.env);if(S){S.snc_lib&&!existsSync(S.snc_lib)&&c.warn({path:S.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."),p=new Gt(S,c);let T=!!S.snc_qop;c.info({ashost:S.ashost,mshost:S.mshost,snc:T,...T?{qop:S.snc_qop,myname:S.snc_myname}:{}},T?"RFC pool ready (SNC-secured)":"RFC pool ready");}else c.info("node-rfc loaded but no SAP_RFC_* config \u2014 RFC tier requires sap_enable_tools rfc + env vars to activate");}let m=new _e,f={exposePatterns:e.expose,idocStore:m,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:S=>{c.info({path:S,attemptedPaths:3},"V2 catalog auto-discovery: matched path");}},g=e.toolTimeoutSeconds??It;g===0?c.warn("Zeitbudget je Tool-Aufruf abgeschaltet (--tool-timeout 0): ein Aufruf kann unbegrenzt laufen und die Client-Verbindung bis zu deren eigenem Timeout blockieren."):c.info({toolTimeoutSeconds:g},"Zeitbudget je Tool-Aufruf aktiv"),e.cacheDir?c.info({cacheDir:e.cacheDir},"Cache-Ablage aktiv (\xFCberlebt Neustarts)"):c.info("Kein Cache-Verzeichnis konfiguriert (--cache-dir / SAP_CACHE_DIR): Katalog und Metadaten werden nach jedem Neustart erneut aus SAP geholt.");let h=await Hr({serverVersion:K,logger:c,env:process.env},{},e.governanceModule);if(h.module){c.info({module:h.module.name,version:h.module.version},"Governance-Modul aktiv: Werkzeugaufrufe werden dem Modul gemeldet (Aufrufjournal).");let S=h.module;if(S.onConfigChange){let T=()=>Lr(o.tokens(),o.names().map(G=>o.get(G)).filter(G=>G!==void 0)),x=T();o.onChange(G=>{let st=T(),it=Br(x,st);if(x=st,it.length!==0)try{S.onConfigChange?.({apiVersion:1,serverVersion:K,at:new Date().toISOString(),version:G.version,changes:it});}catch(Ue){c.error({err:Ue},"Governance-Modul: Konfigurations\xE4nderung konnte nicht gemeldet werden");}});}}else {let S=Wr(process.env);S&&c.info(S.fields,S.msg);}let b={...h.module?{governance:h.module}:{},...f,toolTimeoutMs:g*1e3,cacheDir:e.cacheDir,readOnly:e.readOnly,tiers:e.tiers,hasIdocConfig:nn(e),hasRfcConfig:Gn(process.env),rfcAvailable:u,rfcPool:p,logger:c},y;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:S}=qt(a,b),T=new StdioServerTransport;if(await S.connect(T),c.info("SAP MCP server connected on stdio transport"),e.webhookPort){e.webhookSecret||c.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 x=await Kr(e.webhookPort,m,{host:e.webhookHost,secret:e.webhookSecret,maxBodyBytes:e.webhookMaxBodyBytes});c.info({port:x.port,auth:e.webhookSecret?"secret":"open"},"IDoc webhook server listening");}}else {let S=e.port??8808,T=!!e.apiKey||d>0;!e.webhookSecret&&!T&&c.warn({port:S},"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(),y=await Ti({transport:n,port:S,apiKey:e.apiKey,adminToken:e.adminToken,env:process.env,destinations:o,serverOptions:b,governanceHealth:()=>h.module?{active:true,module:h.module.name,version:h.module.version,...h.module.health?.()??{}}:{active:false},jwtIssuer:process.env.SAP_JWT_ISSUER,jwtAudience:process.env.SAP_JWT_AUDIENCE,idocStore:m,webhookSecret:e.webhookSecret,webhookMaxBodyBytes:e.webhookMaxBodyBytes,maxRequestBytes:e.maxRequestBytes}),c.info({transport:n,port:y.port,auth:[...e.apiKey?["api-key"]:[],...d>0?[`tokens(${d})`]:[]].join("+")||"open",adminApi:e.adminToken?"/admin (admin-token)":"off"},"SAP MCP server listening on HTTP"),e.adminToken&&c.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 S of o.names()){let T=o.get(S);if(!T)continue;let x=o.names().length>1?c.child({destination:S}):c;if(T.authType==="user-basic"){x.info({baseUrl:T.baseUrl,sapClient:T.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}lr(x,T,l);}let E=async S=>{c.info({signal:S},"Shutting down...");try{o.close(),p&&await p.closeAll(),y&&await y.close(),s&&await s.close(),h.module?.close&&await h.module.close();}catch(T){c.error({err:T},"Shutdown error (continuing)");}process.exit(0);};process.on("SIGTERM",()=>E("SIGTERM")),process.on("SIGINT",()=>E("SIGINT"));}function bl(t){let e=Tr(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}}Sl().catch(t=>{process.stderr.write(`Fatal error: ${t instanceof Error?t.message:String(t)}
|
|
410
412
|
`),process.exit(1);});
|