guniweb-sap-mcp 0.2.0 → 0.2.1
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 +24 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {existsSync,readFileSync}from'fs';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import In from'pino';import {minimatch}from'minimatch';import {z as z$1}from'zod/v4';import {z as z$2}from'zod';import {createServer}from'http';import {XMLParser,XMLBuilder}from'fast-xml-parser';import {McpServer,ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import
|
|
2
|
+
import {existsSync,readFileSync}from'fs';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import In from'pino';import {minimatch}from'minimatch';import {z as z$1}from'zod/v4';import {z as z$2}from'zod';import {createServer}from'http';import {XMLParser,XMLBuilder}from'fast-xml-parser';import {McpServer,ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import Ie,{isAxiosError}from'axios';import {randomUUID,createHash}from'crypto';import {decodeJwt,createRemoteJWKSet,jwtVerify}from'jose';import {SignedXml}from'xml-crypto';import {Agent}from'https';import {createMcpExpressApp}from'@modelcontextprotocol/sdk/server/express.js';import {SSEServerTransport}from'@modelcontextprotocol/sdk/server/sse.js';import {StreamableHTTPServerTransport}from'@modelcontextprotocol/sdk/server/streamableHttp.js';function G(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 Ve(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 en(t,e)}function en(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("--transport");if(r!==-1&&r<t.length-1){let E=t[r+1];(E==="stdio"||E==="http"||E==="sse")&&(e.transport=E);}let s=t.indexOf("--port");s!==-1&&s<t.length-1&&(e.port=Number.parseInt(t[s+1],10));let i=t.indexOf("--api-key");i!==-1&&i<t.length-1&&(e.apiKey=t[i+1]);let o=["sndpor","sndprt","sndprn","rcvpor","rcvprt","rcvprn"];for(let E of o){let D=t.indexOf(`--${E}`);D!==-1&&D<t.length-1&&(e[E]=t[D+1]);}let a=t.indexOf("--catalog-v2-url");a!==-1&&a<t.length-1&&(e.catalogV2Url=t[a+1]),!e.catalogV2Url&&process.env.ODATA_V2_CATALOG_PATH&&(e.catalogV2Url=process.env.ODATA_V2_CATALOG_PATH);let c=t.indexOf("--catalog-v4-url");c!==-1&&c<t.length-1&&(e.catalogV4Url=t[c+1]),t.indexOf("--read-only")!==-1&&(e.readOnly=true),process.env.SAP_MCP_READ_ONLY==="true"&&(e.readOnly=true);let d=t.indexOf("--help"),l=t.indexOf("-h");(d!==-1||l!==-1)&&(e.help=true);let u=t.indexOf("--tiers");if(u!==-1&&u<t.length-1){let E=t[u+1].split(",").map(D=>D.trim()).filter(D=>D.length>0);e.tiers=E.length>0?E:void 0;}let m=t.indexOf("--destination-name");m!==-1&&m<t.length-1&&(e.destinationName=t[m+1]),!e.destinationName&&process.env.SAP_DESTINATION_NAME&&(e.destinationName=process.env.SAP_DESTINATION_NAME);let g=t.indexOf("--destination-service-url");g!==-1&&g<t.length-1&&(e.destinationServiceUrl=t[g+1]),!e.destinationServiceUrl&&process.env.SAP_DESTINATION_SERVICE_URL&&(e.destinationServiceUrl=process.env.SAP_DESTINATION_SERVICE_URL);let f=t.indexOf("--issuer");f!==-1&&f<t.length-1&&(e.issuer=t[f+1]),!e.issuer&&process.env.SAP_ISSUER&&(e.issuer=process.env.SAP_ISSUER);let h=t.indexOf("--name-id-format");h!==-1&&h<t.length-1&&(e.nameIdFormat=t[h+1]),!e.nameIdFormat&&process.env.SAP_NAME_ID_FORMAT&&(e.nameIdFormat=process.env.SAP_NAME_ID_FORMAT);let R=t.indexOf("--private-key"),S=R!==-1&&R<t.length-1?t[R+1]:void 0;e.privateKey=G(S,"SAP_PRIVATE_KEY","SAP_PRIVATE_KEY_FILE");let T=t.indexOf("--certificate"),O=T!==-1&&T<t.length-1?t[T+1]:void 0;e.certificate=G(O,"SAP_CERTIFICATE","SAP_CERTIFICATE_FILE");let W=t.indexOf("--clock-skew");W!==-1&&W<t.length-1&&(e.clockSkew=Number.parseInt(t[W+1],10)),e.clockSkew===void 0&&process.env.SAP_CLOCK_SKEW&&(e.clockSkew=Number.parseInt(process.env.SAP_CLOCK_SKEW,10));let j=t.indexOf("--assertion-lifetime");return j!==-1&&j<t.length-1&&(e.assertionLifetime=Number.parseInt(t[j+1],10)),e.assertionLifetime===void 0&&process.env.SAP_ASSERTION_LIFETIME&&(e.assertionLifetime=Number.parseInt(process.env.SAP_ASSERTION_LIFETIME,10)),e}function Be(t){return !!(t.sndprn&&t.rcvprn)}function M(t,e){return !e||e.length===0?true:e.some(n=>minimatch(t,n))}var qe=z$1.object({tlsVerify:z$1.boolean().default(true),clientCert:z$1.string().optional(),clientKey:z$1.string().optional(),clientKeyPassphrase: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 Ce(t=process.env){let e=t.SAP_TLS_VERIFY!=="false",n=G(void 0,"SAP_CLIENT_CERT","SAP_CLIENT_CERT_PATH",t),r=G(void 0,"SAP_CLIENT_KEY","SAP_CLIENT_KEY_PATH",t),s=t.SAP_CLIENT_KEY_PASSPHRASE,i={tlsVerify:e,...n!==void 0?{clientCert:n}:{},...r!==void 0?{clientKey:r}:{},...s!==void 0?{clientKeyPassphrase:s}:{}},o=qe.safeParse(i);if(!o.success){let a=o.error.issues.map(c=>` - ${c.path.join(".")||"root"}: ${c.message}`).join(`
|
|
3
3
|
`);throw new Error(`Invalid HTTP options:
|
|
4
|
-
${a}`)}return o.data}var U=z$2.object({name:z$2.string().default("default"),baseUrl:z$2.url(),sapClient:z$2.string().optional()}),tn=U.extend({authType:z$2.literal("basic"),username:z$2.string().min(1),password:z$2.string().min(1)}),nn=U.extend({authType:z$2.literal("oauth2"),clientId:z$2.string().min(1),clientSecret:z$2.string().min(1),tokenServiceUrl:z$2.url()}),rn=U.extend({authType:z$2.literal("apikey"),apiKey:z$2.string().min(1)}),sn=U.extend({authType:z$2.literal("ias"),tenantUrl:z$2.url(),clientId:z$2.string().min(1),clientSecret:z$2.string().min(1)}),on=U.extend({authType:z$2.literal("xsuaa"),xsuaaUrl:z$2.url(),clientId:z$2.string().min(1),clientSecret:z$2.string().min(1)}),an=U.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()}),cn=U.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)}),Ae=z$2.discriminatedUnion("authType",[tn,nn,rn,sn,on,an,cn]);function
|
|
4
|
+
${a}`)}return o.data}var U=z$2.object({name:z$2.string().default("default"),baseUrl:z$2.url(),sapClient:z$2.string().optional()}),tn=U.extend({authType:z$2.literal("basic"),username:z$2.string().min(1),password:z$2.string().min(1)}),nn=U.extend({authType:z$2.literal("oauth2"),clientId:z$2.string().min(1),clientSecret:z$2.string().min(1),tokenServiceUrl:z$2.url()}),rn=U.extend({authType:z$2.literal("apikey"),apiKey:z$2.string().min(1)}),sn=U.extend({authType:z$2.literal("ias"),tenantUrl:z$2.url(),clientId:z$2.string().min(1),clientSecret:z$2.string().min(1)}),on=U.extend({authType:z$2.literal("xsuaa"),xsuaaUrl:z$2.url(),clientId:z$2.string().min(1),clientSecret:z$2.string().min(1)}),an=U.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()}),cn=U.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)}),Ae=z$2.discriminatedUnion("authType",[tn,nn,rn,sn,on,an,cn]);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=Ae.safeParse(n);if(!r.success){let s=r.error.issues.map(i=>` - ${i.path.join(".")||"root"}: ${i.message}`).join(`
|
|
5
5
|
`);throw new Error(`Invalid SAP connection configuration:
|
|
6
6
|
${s}
|
|
7
7
|
|
|
8
|
-
Set the required SAP_* environment variables.`)}return r.data}var F=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 ln=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",parseTagValue:false,isArray:t=>t==="IDOC"||t.startsWith("E1")||t.startsWith("E2")}),K=new Set(["@_SEGMENT","@_BEGIN"]);function Ce(t,e){return !!(t.startsWith("E1")||t.startsWith("E2")||typeof e=="object"&&e!==null&&"@_SEGMENT"in e)}function We(t){let e=[];for(let[n,r]of Object.entries(t)){if(K.has(n)||!Ce(n,r))continue;let s=Array.isArray(r)?r:[r];for(let i of s){if(typeof i!="object"||i===null)continue;let o={},a=[];for(let[c,p]of Object.entries(i))if(!K.has(c))if(Ce(c,p)){let u=Array.isArray(p)?p:[p];for(let l of u){if(typeof l!="object"||l===null)continue;let d={},m=We(l);for(let[g,f]of Object.entries(l))K.has(g)||!Ce(g,f)&&typeof f!="object"&&(d[g]=String(f));a.push({name:c,fields:d,...m.length>0?{children:m}:{}});}}else typeof p!="object"&&(o[c]=String(p));e.push({name:n,fields:o,...a.length>0?{children:a}:{}});}}return e}function Ge(t){let e=ln.parse(t),n=Object.keys(e).find(l=>l!=="?xml");if(!n)throw new Error("Invalid IDoc XML: no root element found");let r=n,i=e[n].IDOC;if(!i||!Array.isArray(i)||i.length===0)throw new Error("Invalid IDoc XML: no IDOC element found");let o=i[0],a=o.EDI_DC40,c={};if(a){let l=Array.isArray(a)?a[0]:a;for(let[d,m]of Object.entries(l))!K.has(d)&&typeof m!="object"&&(c[d]=String(m));}let p={};for(let[l,d]of Object.entries(o))l==="EDI_DC40"||K.has(l)||(p[l]=d);let u=We(p);return {idocType:r,controlRecord:c,segments:u}}function dn(t){return new Promise((e,n)=>{let r="";t.on("data",s=>{r+=s.toString();}),t.on("end",()=>e(r)),t.on("error",n);})}function oe(t){return (e,n)=>{if(e.method!=="POST"){n.writeHead(405,{"Content-Type":"text/plain"}),n.end("Method Not Allowed");return}dn(e).then(r=>{let{idocType:s,controlRecord:i,segments:o}=Ge(r),a={docnum:i.DOCNUM||`UNKNOWN_${Date.now()}`,idocType:s,mesType:i.MESTYP||"",receivedAt:new Date,rawXml:r,parsed:{controlRecord:i,segments:o}};t.add(a),n.writeHead(200,{"Content-Type":"text/plain"}),n.end("OK");}).catch(r=>{n.writeHead(400,{"Content-Type":"text/plain"}),n.end(`Parse error: ${r instanceof Error?r.message:String(r)}`);});}}function Ke(t,e){let n=oe(e);return new Promise((r,s)=>{let i=createServer((o,a)=>{o.method==="POST"&&o.url==="/idoc"?n(o,a):(a.writeHead(404,{"Content-Type":"text/plain"}),a.end("Not Found"));});i.listen(t,()=>{let o=i.address(),a=typeof o=="object"&&o!==null?o.port:t;r({port:a,close:()=>new Promise((c,p)=>{i.close(u=>u?p(u):c());})});}),i.on("error",s);})}function Ye(t){let e={name:t.name,url:t.baseUrl,sapClient:t.sapClient};switch(t.authType){case "basic":return {...e,auth:{type:"basic",username:t.username,password:t.password}};case "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 $=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 Ee.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 Pe(t){return createHash("sha256").update(t).digest("hex")}var Y=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});}},ae=class{clientTokenManager;cache;config;constructor(e){this.config=e,this.clientTokenManager=new $(e.tokenServiceUrl,e.clientId,e.clientSecret),this.cache=new Y;}async getToken(e){if(!e)return this.clientTokenManager.getToken();let n=Pe(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 Ee.get(s,{headers:{Authorization:`Bearer ${r}`,"X-user-token":e}})).data.authTokens;if(!o?.length)throw new Error(`Destination '${this.config.destinationName}': no authTokens in response`);let a=o[0];if(a.error)throw new Error(`Destination '${this.config.destinationName}' token exchange failed: ${a.error}`);let c=parseInt(a.expires_in,10),p=Number.isFinite(c)&&c>0?c:3600,u=Date.now()+(p-60)*1e3;return this.cache.set(n,a.value,u),a.value}};var J=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 Ee.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 z=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 Ee.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 Cn={email:"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",sapUsername:"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"};function Je(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 Pn(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 bn(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 ce=class{cache;config;constructor(e){Je(e.privateKey,"private key"),Je(e.certificate,"certificate"),this.config={...e,clockSkew:e.clockSkew??120,assertionLifetime:e.assertionLifetime??300},this.cache=new Y;}async getToken(e){if(!e)throw new Error("saml-bearer auth requires a user token for principal propagation");let n=Pe(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=Cn[this.config.nameIdFormat],a=new Date(s-this.config.clockSkew*1e3).toISOString(),c=new Date(s+this.config.assertionLifetime*1e3).toISOString(),p=new Date(s).toISOString(),u=Pn({assertionId:`_${randomUUID()}`,issuer:this.config.issuer,nameId:i,nameIdFormat:o,recipient:this.config.tokenServiceUrl,audience:this.config.clientId,notBefore:a,notOnOrAfter:c,authnInstant:p}),l=bn(u,this.config.privateKey,this.config.certificate),d=Buffer.from(l).toString("base64"),m=await Ee.post(this.config.tokenServiceUrl,new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:saml2-bearer",assertion:d,client_id:this.config.clientId}).toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}}),{access_token:g,expires_in:f}=m.data,v=typeof f=="number"&&f>0?f:3600,h=Date.now()+(v-60)*1e3;return this.cache.set(n,g,h),g}};function be(t){if(t==null)return {code:"UNKNOWN",message:"Unknown SAP error",details:[],rawResponse:t};if(typeof t=="string")return {code:"UNKNOWN",message:t,details:[],rawResponse:t};let n=t.error;if(!n)return {code:"UNKNOWN",message:"Unknown SAP error",details:[],rawResponse:t};let r=n.message,s;r&&typeof r=="object"&&"value"in r?s=r.value??"Unknown error":typeof r=="string"?s=r:s="Unknown error";let a=(n.innererror?.errordetails??[]).map(c=>({code:c.code??"",message:c.message??"",severity:c.severity??"error",target:c.target}));return {code:n.code??"UNKNOWN",message:s,target:n.target,details:a,rawResponse:t}}function ze(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 Q=In({name:"sap-http-client",transport:{target:"pino/file",options:{destination:2}}}),pe=class{constructor(e,n={}){this.destination=e;switch(this.httpsAgent=ze(n),e.auth.type){case "oauth2":this.tokenManager=new $(e.auth.tokenServiceUrl,e.auth.clientId,e.auth.clientSecret);break;case "ias":this.tokenManager=new J({issuerUrl:e.auth.tenantUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new z);break;case "xsuaa":this.tokenManager=new J({issuerUrl:e.auth.xsuaaUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new z);break;case "btp-principal":this.tokenManager=new ae({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 ce({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;async request(e){let n=e.method!=="get",r={...e.params};this.destination.sapClient&&(r["sap-client"]=this.destination.sapClient);let s=e.userContext??this.userContext??void 0,i=await this.buildAuthHeaders(s),o=this.destination.headers??{},a={};if(n){let l=await this.fetchCsrfToken({...o,...i},e.url);l.token&&(a={"x-csrf-token":l.token},l.cookies&&(a.Cookie=l.cookies));}let c={...o,...i,...a,...e.headers},p={method:e.method,url:`${this.destination.url}${e.url}`,data:e.data,headers:c,params:Object.keys(r).length>0?r:void 0,httpsAgent:this.httpsAgent,beforeRedirect:l=>{this.httpsAgent&&(l.agent=this.httpsAgent);let d=l.headers??{};for(let[m,g]of Object.entries(c))d[m]||(d[m]=g);}},u={...p.headers};u.Authorization&&(u.Authorization=`${u.Authorization.substring(0,15)}***`),Q.info({method:p.method,url:p.url,headers:u,params:p.params,hasAuth:!!p.headers.Authorization||!!p.headers.APIKey},"SAP HTTP request outgoing");try{let l=await Ee(p);if(Q.info({status:l.status,contentType:l.headers["content-type"]},"SAP HTTP response OK"),typeof l.data=="string"&&l.data.trimStart().startsWith("<!DOCTYPE"))throw new Error("SAP returned an HTML page instead of JSON. This usually means authentication failed or the URL path is invalid.");return {data:l.data,headers:l.headers,status:l.status}}catch(l){if(isAxiosError(l)&&l.response?Q.error({status:l.response.status,statusText:l.response.statusText,contentType:l.response.headers?.["content-type"],url:p.url,dataPreview:typeof l.response.data=="string"?l.response.data.substring(0,200):typeof l.response.data},"SAP HTTP response ERROR"):l instanceof Error&&Q.error({message:l.message,name:l.name},"SAP HTTP connection error"),n&&this.isCsrf403(l))try{let d=await this.fetchCsrfToken({...o,...i},e.url),m={...p.headers};d.token&&(m["x-csrf-token"]=d.token,d.cookies&&(m.Cookie=d.cookies));let g=await Ee({...p,headers:m});return {data:g.data,headers:g.headers,status:g.status}}catch(d){throw this.toSapError(d)}throw this.toSapError(l)}}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 {}}}async fetchCsrfToken(e,n){let r=this.destination.url,s=[];if(n){let i=n.replace(/\/[^/]*$/,"/");i!=="/"&&i!==n&&s.push(`${r}${i}`);}s.push(`${r}/`,r);for(let i of s)try{let o={...e,"x-csrf-token":"fetch"},a={};this.destination.sapClient&&(a["sap-client"]=this.destination.sapClient);let c=await Ee.get(i,{headers:o,params:Object.keys(a).length>0?a:void 0,validateStatus:()=>!0,httpsAgent:this.httpsAgent,beforeRedirect:u=>{this.httpsAgent&&(u.agent=this.httpsAgent);let l=u.headers??{};for(let[d,m]of Object.entries(o))l[d]||(l[d]=m);}}),p=c.headers["x-csrf-token"];if(Q.debug({url:i,status:c.status,hasToken:!!p,tokenValue:p?.substring(0,8)},"CSRF token fetch result"),p&&p.toLowerCase()!=="required"){let l=(c.headers["set-cookie"]??[]).map(d=>d.split(";")[0]).join("; ");return {token:p,cookies:l}}}catch{}return {token:"",cookies:""}}isCsrf403(e){return isAxiosError(e)&&e.response?.status===403}toSapError(e){return isAxiosError(e)&&e.response?be(e.response.data):e instanceof Error?{code:"CONNECTION_ERROR",message:e.message,details:[],rawResponse:void 0}:be(e)}};var we=0;function Qe(t){let e=t.docnum??`${Date.now().toString().padStart(13,"0")}${String(we++).padStart(3,"0")}`;we>=1e3&&(we=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 wn(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 On(t,e){return t.filter(n=>n.name===e).length}function Xe(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=On(t,s.name);o>i.maxOccurrence&&n.push({segmentName:s.name,fieldName:"",expectedType:"",expectedLength:i.maxOccurrence,actualValue:String(o),violation:`Segment count ${o} exceeds maxOccurrence ${i.maxOccurrence}`});}for(let s of t){let i=e.get(s.name);if(!i){n.push({segmentName:s.name,fieldName:"",expectedType:"",expectedLength:0,actualValue:"",violation:`Unknown segment '${s.name}' not found in metadata`});continue}for(let o of i.fields){let a=s.fields[o.name]??"",c=wn(s.name,o,a);c&&n.push(c);}s.children&&s.children.length>0&&Xe(s.children,e,n);}}function Ze(t,e){if(!e)return [];let n=new Map;for(let s of e.segments)n.set(s.name,s);let r=[];return Xe(t,n,r),r}var xn=new XMLBuilder({ignoreAttributes:false,attributeNamePrefix:"@_",format:true,suppressEmptyNode:false});function et(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=et(n),s=e[n.name];s?Array.isArray(s)?s.push(r):e[n.name]=[s,r]:e[n.name]=r;}return e}function tt(t,e,n){let r={};for(let i of n){let o=et(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 xn.build(s)}var le=class{constructor(e,n,r){this.httpClient=e;this.sapClient=n;this.partnerDefaults=r;}async send(e){let n={...this.partnerDefaults,...e.partnerOverrides},r=Qe({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 l=Ze(e.segments,e.metadata);if(l.length>0)return {success:false,docnum:r.DOCNUM,message:"Validation failed",violations:l}}let s=tt(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 u=a.match(/<title>(.*?)<\/title>/)?.[1]??"IDoc send failed";return {success:false,docnum:r.DOCNUM,message:u}}};function nt(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?"":`
|
|
8
|
+
Set the required SAP_* environment variables.`)}return r.data}var F=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 ln=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",parseTagValue:false,isArray:t=>t==="IDOC"||t.startsWith("E1")||t.startsWith("E2")}),Y=new Set(["@_SEGMENT","@_BEGIN"]);function Pe(t,e){return !!(t.startsWith("E1")||t.startsWith("E2")||typeof e=="object"&&e!==null&&"@_SEGMENT"in e)}function Ge(t){let e=[];for(let[n,r]of Object.entries(t)){if(Y.has(n)||!Pe(n,r))continue;let s=Array.isArray(r)?r:[r];for(let i of s){if(typeof i!="object"||i===null)continue;let o={},a=[];for(let[c,p]of Object.entries(i))if(!Y.has(c))if(Pe(c,p)){let d=Array.isArray(p)?p:[p];for(let l of d){if(typeof l!="object"||l===null)continue;let u={},m=Ge(l);for(let[g,f]of Object.entries(l))Y.has(g)||!Pe(g,f)&&typeof f!="object"&&(u[g]=String(f));a.push({name:c,fields:u,...m.length>0?{children:m}:{}});}}else typeof p!="object"&&(o[c]=String(p));e.push({name:n,fields:o,...a.length>0?{children:a}:{}});}}return e}function Ke(t){let e=ln.parse(t),n=Object.keys(e).find(l=>l!=="?xml");if(!n)throw new Error("Invalid IDoc XML: no root element found");let r=n,i=e[n].IDOC;if(!i||!Array.isArray(i)||i.length===0)throw new Error("Invalid IDoc XML: no IDOC element found");let o=i[0],a=o.EDI_DC40,c={};if(a){let l=Array.isArray(a)?a[0]:a;for(let[u,m]of Object.entries(l))!Y.has(u)&&typeof m!="object"&&(c[u]=String(m));}let p={};for(let[l,u]of Object.entries(o))l==="EDI_DC40"||Y.has(l)||(p[l]=u);let d=Ge(p);return {idocType:r,controlRecord:c,segments:d}}function dn(t){return new Promise((e,n)=>{let r="";t.on("data",s=>{r+=s.toString();}),t.on("end",()=>e(r)),t.on("error",n);})}function ae(t){return (e,n)=>{if(e.method!=="POST"){n.writeHead(405,{"Content-Type":"text/plain"}),n.end("Method Not Allowed");return}dn(e).then(r=>{let{idocType:s,controlRecord:i,segments:o}=Ke(r),a={docnum:i.DOCNUM||`UNKNOWN_${Date.now()}`,idocType:s,mesType:i.MESTYP||"",receivedAt:new Date,rawXml:r,parsed:{controlRecord:i,segments:o}};t.add(a),n.writeHead(200,{"Content-Type":"text/plain"}),n.end("OK");}).catch(r=>{n.writeHead(400,{"Content-Type":"text/plain"}),n.end(`Parse error: ${r instanceof Error?r.message:String(r)}`);});}}function Ye(t,e){let n=ae(e);return new Promise((r,s)=>{let i=createServer((o,a)=>{o.method==="POST"&&o.url==="/idoc"?n(o,a):(a.writeHead(404,{"Content-Type":"text/plain"}),a.end("Not Found"));});i.listen(t,()=>{let o=i.address(),a=typeof o=="object"&&o!==null?o.port:t;r({port:a,close:()=>new Promise((c,p)=>{i.close(d=>d?p(d):c());})});}),i.on("error",s);})}function Je(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 "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 $=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 Ie.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 be(t){return createHash("sha256").update(t).digest("hex")}var J=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});}},ce=class{clientTokenManager;cache;config;constructor(e){this.config=e,this.clientTokenManager=new $(e.tokenServiceUrl,e.clientId,e.clientSecret),this.cache=new J;}async getToken(e){if(!e)return this.clientTokenManager.getToken();let n=be(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 Ie.get(s,{headers:{Authorization:`Bearer ${r}`,"X-user-token":e}})).data.authTokens;if(!o?.length)throw new Error(`Destination '${this.config.destinationName}': no authTokens in response`);let a=o[0];if(a.error)throw new Error(`Destination '${this.config.destinationName}' token exchange failed: ${a.error}`);let c=parseInt(a.expires_in,10),p=Number.isFinite(c)&&c>0?c:3600,d=Date.now()+(p-60)*1e3;return this.cache.set(n,a.value,d),a.value}};var z=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 Ie.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 Q=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 Ie.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 An={email:"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",sapUsername:"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"};function ze(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 Pn(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 bn(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 pe=class{cache;config;constructor(e){ze(e.privateKey,"private key"),ze(e.certificate,"certificate"),this.config={...e,clockSkew:e.clockSkew??120,assertionLifetime:e.assertionLifetime??300},this.cache=new J;}async getToken(e){if(!e)throw new Error("saml-bearer auth requires a user token for principal propagation");let n=be(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=An[this.config.nameIdFormat],a=new Date(s-this.config.clockSkew*1e3).toISOString(),c=new Date(s+this.config.assertionLifetime*1e3).toISOString(),p=new Date(s).toISOString(),d=Pn({assertionId:`_${randomUUID()}`,issuer:this.config.issuer,nameId:i,nameIdFormat:o,recipient:this.config.tokenServiceUrl,audience:this.config.clientId,notBefore:a,notOnOrAfter:c,authnInstant:p}),l=bn(d,this.config.privateKey,this.config.certificate),u=Buffer.from(l).toString("base64"),m=await Ie.post(this.config.tokenServiceUrl,new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:saml2-bearer",assertion:u,client_id:this.config.clientId}).toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}}),{access_token:g,expires_in:f}=m.data,h=typeof f=="number"&&f>0?f:3600,R=Date.now()+(h-60)*1e3;return this.cache.set(n,g,R),g}};function Ee(t){if(t==null)return {code:"UNKNOWN",message:"Unknown SAP error",details:[],rawResponse:t};if(typeof t=="string")return {code:"UNKNOWN",message:t,details:[],rawResponse:t};let n=t.error;if(!n)return {code:"UNKNOWN",message:"Unknown SAP error",details:[],rawResponse:t};let r=n.message,s;r&&typeof r=="object"&&"value"in r?s=r.value??"Unknown error":typeof r=="string"?s=r:s="Unknown error";let a=(n.innererror?.errordetails??[]).map(c=>({code:c.code??"",message:c.message??"",severity:c.severity??"error",target:c.target}));return {code:n.code??"UNKNOWN",message:s,target:n.target,details:a,rawResponse:t}}function Qe(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 X=In({name:"sap-http-client",transport:{target:"pino/file",options:{destination:2}}}),le=class{constructor(e,n={}){this.destination=e;switch(this.httpsAgent=Qe(n),e.auth.type){case "oauth2":this.tokenManager=new $(e.auth.tokenServiceUrl,e.auth.clientId,e.auth.clientSecret);break;case "ias":this.tokenManager=new z({issuerUrl:e.auth.tenantUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new Q);break;case "xsuaa":this.tokenManager=new z({issuerUrl:e.auth.xsuaaUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new Q);break;case "btp-principal":this.tokenManager=new ce({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 pe({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;async request(e){let n=e.method!=="get",r={...e.params};this.destination.sapClient&&(r["sap-client"]=this.destination.sapClient);let s=e.userContext??this.userContext??void 0,i=await this.buildAuthHeaders(s),o=this.destination.headers??{},a={};if(n){let l=await this.fetchCsrfToken({...o,...i},e.url);l.token&&(a={"x-csrf-token":l.token},l.cookies&&(a.Cookie=l.cookies));}let c={...o,...i,...a,...e.headers},p={method:e.method,url:`${this.destination.url}${e.url}`,data:e.data,headers:c,params:Object.keys(r).length>0?r:void 0,httpsAgent:this.httpsAgent,beforeRedirect:l=>{this.httpsAgent&&(l.agent=this.httpsAgent);let u=l.headers??{};for(let[m,g]of Object.entries(c))u[m]||(u[m]=g);}},d={...p.headers};d.Authorization&&(d.Authorization=`${d.Authorization.substring(0,15)}***`),X.info({method:p.method,url:p.url,headers:d,params:p.params,hasAuth:!!p.headers.Authorization||!!p.headers.APIKey},"SAP HTTP request outgoing");try{let l=await Ie(p);if(X.info({status:l.status,contentType:l.headers["content-type"]},"SAP HTTP response OK"),typeof l.data=="string"&&l.data.trimStart().startsWith("<!DOCTYPE"))throw new Error("SAP returned an HTML page instead of JSON. This usually means authentication failed or the URL path is invalid.");return {data:l.data,headers:l.headers,status:l.status}}catch(l){if(isAxiosError(l)&&l.response?X.error({status:l.response.status,statusText:l.response.statusText,contentType:l.response.headers?.["content-type"],url:p.url,dataPreview:typeof l.response.data=="string"?l.response.data.substring(0,200):typeof l.response.data},"SAP HTTP response ERROR"):l instanceof Error&&X.error({message:l.message,name:l.name},"SAP HTTP connection error"),n&&this.isCsrf403(l))try{let u=await this.fetchCsrfToken({...o,...i},e.url),m={...p.headers};u.token&&(m["x-csrf-token"]=u.token,u.cookies&&(m.Cookie=u.cookies));let g=await Ie({...p,headers:m});return {data:g.data,headers:g.headers,status:g.status}}catch(u){throw this.toSapError(u)}throw this.toSapError(l)}}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 {}}}async fetchCsrfToken(e,n){let r=this.destination.url,s=[];if(n){let i=n.replace(/\/[^/]*$/,"/");i!=="/"&&i!==n&&s.push(`${r}${i}`);}s.push(`${r}/`,r);for(let i of s)try{let o={...e,"x-csrf-token":"fetch"},a={};this.destination.sapClient&&(a["sap-client"]=this.destination.sapClient);let c=await Ie.get(i,{headers:o,params:Object.keys(a).length>0?a:void 0,validateStatus:()=>!0,httpsAgent:this.httpsAgent,beforeRedirect:d=>{this.httpsAgent&&(d.agent=this.httpsAgent);let l=d.headers??{};for(let[u,m]of Object.entries(o))l[u]||(l[u]=m);}}),p=c.headers["x-csrf-token"];if(X.debug({url:i,status:c.status,hasToken:!!p,tokenValue:p?.substring(0,8)},"CSRF token fetch result"),p&&p.toLowerCase()!=="required"){let l=(c.headers["set-cookie"]??[]).map(u=>u.split(";")[0]).join("; ");return {token:p,cookies:l}}}catch{}return {token:"",cookies:""}}isCsrf403(e){return isAxiosError(e)&&e.response?.status===403}toSapError(e){return isAxiosError(e)&&e.response?Ee(e.response.data):e instanceof Error?{code:"CONNECTION_ERROR",message:e.message,details:[],rawResponse:void 0}:Ee(e)}};var Oe=0;function Xe(t){let e=t.docnum??`${Date.now().toString().padStart(13,"0")}${String(Oe++).padStart(3,"0")}`;Oe>=1e3&&(Oe=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 wn(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 On(t,e){return t.filter(n=>n.name===e).length}function Ze(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=On(t,s.name);o>i.maxOccurrence&&n.push({segmentName:s.name,fieldName:"",expectedType:"",expectedLength:i.maxOccurrence,actualValue:String(o),violation:`Segment count ${o} exceeds maxOccurrence ${i.maxOccurrence}`});}for(let s of t){let i=e.get(s.name);if(!i){n.push({segmentName:s.name,fieldName:"",expectedType:"",expectedLength:0,actualValue:"",violation:`Unknown segment '${s.name}' not found in metadata`});continue}for(let o of i.fields){let a=s.fields[o.name]??"",c=wn(s.name,o,a);c&&n.push(c);}s.children&&s.children.length>0&&Ze(s.children,e,n);}}function et(t,e){if(!e)return [];let n=new Map;for(let s of e.segments)n.set(s.name,s);let r=[];return Ze(t,n,r),r}var xn=new XMLBuilder({ignoreAttributes:false,attributeNamePrefix:"@_",format:true,suppressEmptyNode:false});function tt(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=tt(n),s=e[n.name];s?Array.isArray(s)?s.push(r):e[n.name]=[s,r]:e[n.name]=r;}return e}function nt(t,e,n){let r={};for(let i of n){let o=tt(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 xn.build(s)}var de=class{constructor(e,n,r){this.httpClient=e;this.sapClient=n;this.partnerDefaults=r;}async send(e){let n={...this.partnerDefaults,...e.partnerOverrides},r=Xe({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 l=et(e.segments,e.metadata);if(l.length>0)return {success:false,docnum:r.DOCNUM,message:"Validation failed",violations:l}}let s=nt(e.idocType,r,e.segments),i="/sap/bc/idoc_xml";this.sapClient&&(i+=`?sap-client=${this.sapClient}`);let o=await this.httpClient.request({method:"post",url:i,data:s,headers:{"Content-Type":"text/xml"}}),a=typeof o.data=="string"?o.data:String(o.data);if(a.includes("<title>IDoc-XML-inbound ok</title>"))return {success:true,docnum:r.DOCNUM,message:"IDoc sent successfully"};let d=a.match(/<title>(.*?)<\/title>/)?.[1]??"IDoc send failed";return {success:false,docnum:r.DOCNUM,message:d}}};function rt(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?"":`
|
|
9
9
|
|
|
10
10
|
IMPORTANT \u2014 READ-ONLY MODE:
|
|
11
11
|
You MUST only generate read queries (sap_query or sap_nl_query). Do NOT use sap_create, sap_update, or sap_delete.
|
|
@@ -66,7 +66,7 @@ Use the \`sap_nl_query\` tool with these parameters:
|
|
|
66
66
|
- "orders over 1000 EUR" \u2192 \`{ field: "TotalAmount", operator: "gt", value: 1000 }\`
|
|
67
67
|
- "products containing 'bolt'" \u2192 \`{ field: "ProductName", operator: "contains", value: "bolt" }\`
|
|
68
68
|
- "invoices since January 2024" \u2192 \`{ field: "InvoiceDate", operator: "ge", value: "2024-01-01" }\`
|
|
69
|
-
${i}`}}]}});}function
|
|
69
|
+
${i}`}}]}});}function st(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:
|
|
70
70
|
|
|
71
71
|
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"}
|
|
72
72
|
2. Use sap_list_services to discover available entity sets
|
|
@@ -94,20 +94,20 @@ Use sap_query with appropriate filters to find the material data.`}}]})),t.regis
|
|
|
94
94
|
1. ${r}
|
|
95
95
|
2. Use sap_list_services to discover entity sets
|
|
96
96
|
3. Use sap_get_metadata to understand the Business Partner data model
|
|
97
|
-
${s}`}}]}});}var A=class{cache=new Map;get(e){return this.cache.get(this.normalizeUrl(e))}set(e,n){this.cache.set(this.normalizeUrl(e),n);}has(e){return this.cache.has(this.normalizeUrl(e))}clear(){this.cache.clear();}normalizeUrl(e){return e.replace(/\/+$/,"")}};var
|
|
97
|
+
${s}`}}]}});}var A=class{cache=new Map;get(e){return this.cache.get(this.normalizeUrl(e))}set(e,n){this.cache.set(this.normalizeUrl(e),n);}has(e){return this.cache.has(this.normalizeUrl(e))}clear(){this.cache.clear();}normalizeUrl(e){return e.replace(/\/+$/,"")}};var ge=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 kn(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(`
|
|
98
98
|
|
|
99
|
-
`),a;o!==-1&&(a=i.substring(o+2).trim());let c={status:n,entitySet:""};if(a&&a.length>0)if(r)try{c.result=JSON.parse(a);}catch{}else c.error=a;return c}function
|
|
99
|
+
`),a;o!==-1&&(a=i.substring(o+2).trim());let c={status:n,entitySet:""};if(a&&a.length>0)if(r)try{c.result=JSON.parse(a);}catch{}else c.error=a;return c}function it(t,e){let n=[],r=t.split(`--${e}`);for(let s of r){let i=s.trim();if(!i||i==="--")continue;let o=i.match(/Content-Type:\s*multipart\/mixed;\s*boundary=([^\s\r\n]+)/i);if(o){let c=o[1],p=i.indexOf(`
|
|
100
100
|
|
|
101
|
-
`);if(p!==-1){let
|
|
102
|
-
`);return
|
|
103
|
-
`)}function
|
|
104
|
-
${
|
|
105
|
-
${
|
|
101
|
+
`);if(p!==-1){let d=i.substring(p+2),l=it(d,c);n.push(...l);}continue}let a=kn(i);a&&n.push(a);}return n}function ot(t,e){let n=e.match(/boundary=([^\s;]+)/);if(!n)return [];let r=n[1],s=t.replace(/\r\n/g,`
|
|
102
|
+
`);return it(s,r)}var Dn=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function at(t){return typeof t=="number"?String(t):typeof t=="string"&&Dn.test(t)?`guid'${t}'`:`'${String(t)}'`}function H(t,e){let n=Object.entries(e);if(n.length===1){let s=n[0];return `${t}(${at(s[1])})`}let r=n.map(([s,i])=>`${s}=${at(i)}`);return `${t}(${r.join(",")})`}function Ne(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 Mn(t){return t.key?H(t.entitySet,t.key):t.entitySet}function pt(t){let e=Mn(t),n=t.method.toUpperCase(),r=[];if(r.push("Content-Type: application/http"),r.push("Content-Transfer-Encoding: binary"),r.push(""),r.push(`${n} ${e} HTTP/1.1`),n==="GET"?r.push("Accept: application/json"):(r.push("Content-Type: application/json"),r.push("Accept: application/json"),t.data&&"__etag"in t.data&&r.push(`If-Match: ${t.data.__etag}`)),r.push(""),t.data&&(n==="POST"||n==="PATCH"||n==="DELETE")){let{__etag:s,...i}=t.data;Object.keys(i).length>0&&r.push(JSON.stringify(i));}return r.join(`\r
|
|
103
|
+
`)}function lt(t,e,n){let r=`batch_${randomUUID()}`,s=[];for(let a of e)s.push(`--${r}\r
|
|
104
|
+
${pt(a)}`);if(n)for(let a of n){let c=`changeset_${randomUUID()}`,p=[];for(let l of a.operations)p.push(`--${c}\r
|
|
105
|
+
${pt(l)}`);p.push(`--${c}--`);let d=p.join(`\r
|
|
106
106
|
`);s.push(`--${r}\r
|
|
107
107
|
Content-Type: multipart/mixed; boundary=${c}\r
|
|
108
108
|
\r
|
|
109
|
-
${
|
|
110
|
-
`),o=`multipart/mixed; boundary=${r}`;return {body:i,contentType:o}}var Ln=new Set(["EntityType","Property","NavigationProperty","Association","EntitySet","AssociationSet","PropertyRef","End","FunctionImport","Parameter"]),jn=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",removeNSPrefix:true,isArray:t=>Ln.has(t)});function lt(t,e){let r=jn.parse(t).Edmx.DataServices.Schema,s=Array.isArray(r)?r[0]:r,i=s.EntityType??[],o=s.Association??[],a=s.EntityContainer,c=a?.EntitySet??[],p=new Map;for(let f of o)p.set(f["@_Name"],f);let u=i.map(f=>{let v=Fn(f.Property??[]),h=(f.Key?.PropertyRef??[]).map(x=>x["@_Name"]),R=v.filter(x=>h.includes(x.name)),C=$n(f.NavigationProperty??[],p);return {name:f["@_Name"],keys:R,properties:v,navigationProperties:C}}),l=c.map(f=>({name:f["@_Name"],entityTypeName:Ne(f["@_EntityType"])})),d=a?.FunctionImport??[],m=Hn(Array.isArray(d)?d:[d]),g={version:"v2",serviceUrl:e,entityTypes:u,entitySets:l};return m.length>0&&(g.functionImports=m),g}function Fn(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 $n(t,e){return t.map(n=>{let r=Ne(n["@_Relationship"]),s=e.get(r),i="Unknown",o=false;if(s){let a=s.End.find(c=>c["@_Role"]===n["@_ToRole"]);a&&(i=Ne(a["@_Type"]),o=a["@_Multiplicity"]==="*");}return {name:n["@_Name"],targetEntityType:i,isCollection:o}})}function Hn(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 Ne(t){let e=t.lastIndexOf(".");return e>=0?t.substring(e+1):t}var Vn=/^\/Date\((-?\d+)([+-]\d{4})?\)\/$/;function X(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 dt(t){let e=Vn.exec(t);if(!e)return t;let n=Number(e[1]);return new Date(n).toISOString()}function L(t){if(t==null)return t;if(Array.isArray(t))return t.map(e=>L(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]=dt(s):n[r]=L(s)));return n}return typeof t=="string"?dt(t):t}var ut=10,Z=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;}async read(e,n,r){let s=Oe(r??{});if(n){let i=`${this.serviceUrl}/${H(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=X(o.data);return {...a,results:a.results.map(c=>L(c))}}return this.fetchWithPagination(`${this.serviceUrl}/${e}`,s,r?.maxPages??ut)}async query(e,n){let r=Oe(n,true),s=n.maxPages??ut;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=X(r.data);return s.results.length>0?L(s.results[0]):{}}async update(e,n,r){let s=`${this.serviceUrl}/${H(e,n)}`,i=this.etagCache.get(e,n)??"*",o=await this.httpClient.request({method:"post",url:s,data:r,headers:{Accept:"application/json","Content-Type":"application/json","X-HTTP-Method":"MERGE","If-Match":i}});o.headers.etag&&this.etagCache.set(e,n,o.headers.etag);}async delete(e,n){let r=`${this.serviceUrl}/${H(e,n)}`,s=this.etagCache.get(e,n)??"*";await this.httpClient.request({method:"delete",url:r,headers:{Accept:"application/json","If-Match":s}}),this.etagCache.update(e,n,void 0);}async functionImport(e,n,r){let s=(r??"POST").toLowerCase(),i={$format:"json"};if(n)for(let[p,u]of Object.entries(n))typeof u=="string"?i[p]=`'${u}'`:i[p]=String(u);let o=await this.httpClient.request({method:s,url:`${this.serviceUrl}/${e}`,headers:{Accept:"application/json"},params:i}),a=X(o.data);if(a.results.length>0)return L(a.results[0]);let c=o.data;return c?.d&&typeof c.d=="object"?c.d:{}}async batch(e,n){let{body:r,contentType:s}=pt(this.serviceUrl,e,n),i=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:r,headers:{"Content-Type":s,Accept:"multipart/mixed"}}),o=i.headers["content-type"]??s,a=it(i.data,o),c=[...e,...(n??[]).flatMap(p=>p.operations)];for(let p=0;p<a.length&&p<c.length;p++)a[p].entitySet=c[p].entitySet;return {results:a}}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml"}});return lt(e.data,this.serviceUrl)}async fetchWithPagination(e,n,r){let s=[],i,o=e,a=n,c=0;for(;o&&c<r;){let l=(await this.httpClient.request({method:"get",url:o,headers:{Accept:"application/json"},params:a})).data,d=X(l),m=d.results.map(f=>L(f));s.push(...m),c===0&&d.count!==void 0&&(i=d.count),c++;let g=l?.d;g&&typeof g.__next=="string"?(o=g.__next,a=void 0):o=void 0;}let p={results:s};return i!==void 0&&(p.count=i),o&&(p.nextLink=o),p}};function mt(t){let n=(typeof t=="string"?JSON.parse(t):t).responses;return !n||!Array.isArray(n)?[]:n.map(r=>{let s=r.status,i=s>=200&&s<300,o={status:s,entitySet:""};return r.body!==void 0&&r.body!==null&&(i?o.result=r.body:o.error=JSON.stringify(r.body)),o})}function gt(t){return typeof t=="string"?`'${t}'`:String(t)}function k(t,e){let n=Object.entries(e);if(n.length===1){let[,s]=n[0];return `${t}(${gt(s)})`}let r=n.map(([s,i])=>`${s}=${gt(i)}`).join(",");return `${t}(${r})`}function ge(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 ft(t,e){let n=[];for(let s=0;s<e.length;s++){let i=e[s],o=i.key?k(i.entitySet,i.key):i.entitySet,a={id:String(s+1),method:i.method.toUpperCase(),url:o};i.changesetId&&(a.atomicityGroup=i.changesetId),i.data&&(i.method==="post"||i.method==="patch"||i.method==="delete")&&(a.headers={"Content-Type":"application/json"},a.body=i.data),n.push(a);}return {body:JSON.stringify({requests:n}),contentType:"application/json"}}function ht(t){let e=t.split(".");return e[e.length-1]}function Bn(t,e){return {name:t,type:e.$Type??"Edm.String",nullable:e.$Nullable??false,maxLength:e.$MaxLength,precision:e.$Precision,scale:e.$Scale}}function yt(t,e){let n=JSON.parse(t),r=[],s=[],i=[];for(let[a,c]of Object.entries(n)){if(a.startsWith("$"))continue;let p=c;for(let[u,l]of Object.entries(p)){let d=l;if(d.$Kind==="Action"||d.$Kind==="Function"){let m=d.$IsBound??false,g=d.$Parameter??[],f=m&&g.length>0?g[0].$Name:void 0,v=(m?g.slice(1):g).map(R=>({name:R.$Name,type:R.$Type??"Edm.String",...R.$Nullable===true?{nullable:true}:{}})),h=d.$ReturnType?d.$ReturnType.$Type:void 0;i.push({name:u,isBound:m,...f?{bindingParameter:f}:{},parameters:v,...h?{returnType:h}:{}});}else if(d.$Kind==="EntityType"){let m=[],g=[];for(let[h,R]of Object.entries(d)){if(h.startsWith("$"))continue;let C=R;C.$Kind==="NavigationProperty"?g.push({name:h,targetEntityType:ht(C.$Type),isCollection:C.$Collection??false}):m.push(Bn(h,C));}let v=(d.$Key??[]).map(h=>{let R=m.find(C=>C.name===h);return R||{name:h,type:"Edm.String",nullable:false}});r.push({name:u,keys:v,properties:m,navigationProperties:g});}else if(d.$Kind==="EntityContainer")for(let[m,g]of Object.entries(d)){if(m.startsWith("$"))continue;let f=g;f.$Collection&&f.$Type&&s.push({name:m,entityTypeName:ht(f.$Type)});}}}let o={version:"v4",serviceUrl:e,entityTypes:r,entitySets:s};return i.length>0&&(o.actions=i),o}var qn=10,fe=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;}async read(e,n,r){if(n){let p=`${this.serviceUrl}/${k(e,n)}`,u=r?ge(r):void 0,l=await this.httpClient.request({method:"get",url:p,params:u}),d=l.data,m=l.headers.etag??l.headers.ETag;return m&&this.etagCache.set(e,n,m),{results:[d]}}let s=r?ge(r):void 0,o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=o.value??[],c=o["@odata.count"];return {results:a,count:c}}async query(e,n){let r=n.maxPages??qn,s=ge(n,true),o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=[...o.value??[]],c=o["@odata.count"],p=o["@odata.nextLink"],u=1;for(;p&&u<r;){let d=this.resolveNextLink(p),g=(await this.httpClient.request({method:"get",url:d})).data,f=g.value??[];a.push(...f),p=g["@odata.nextLink"],u++;}let l={results:a,count:c};return p&&(l.nextLink=p),l}async create(e,n){return (await this.httpClient.request({method:"post",url:`${this.serviceUrl}/${e}`,data:n,headers:{"Content-Type":"application/json"}})).data}async update(e,n,r){let s=`${this.serviceUrl}/${k(e,n)}`,i=this.etagCache.get(e,n)??"*",o=await this.httpClient.request({method:"patch",url:s,data:r,headers:{"Content-Type":"application/json","If-Match":i}}),a=o.headers.etag??o.headers.ETag;a&&this.etagCache.update(e,n,a);}async delete(e,n){let r=`${this.serviceUrl}/${k(e,n)}`,s=this.etagCache.get(e,n)??"*";await this.httpClient.request({method:"delete",url:r,headers:{"If-Match":s}});}async batch(e){let{body:n,contentType:r}=ft(this.serviceUrl,e),s=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:n,headers:{"Content-Type":r,Accept:"application/json"}}),i=mt(s.data);for(let o=0;o<i.length&&o<e.length;o++)i[o].entitySet=e[o].entitySet;return {results:i}}async callAction(e,n,r,s){let i;return r&&s?i=`${this.serviceUrl}/${k(r,s)}/${e}`:i=`${this.serviceUrl}/${e}`,(await this.httpClient.request({method:"post",url:i,data:n??{},headers:{"Content-Type":"application/json"}})).data??{}}async callFunction(e,n,r,s){let i=this.buildFunctionParams(n),o;return r&&s?o=`${this.serviceUrl}/${k(r,s)}/${e}(${i})`:o=`${this.serviceUrl}/${e}(${i})`,(await this.httpClient.request({method:"get",url:o})).data??{}}buildFunctionParams(e){return !e||Object.keys(e).length===0?"":Object.entries(e).map(([n,r])=>typeof r=="string"?`${n}='${r}'`:`${n}=${String(r)}`).join(",")}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/json"}}),n=typeof e.data=="string"?e.data:JSON.stringify(e.data);return yt(n,this.serviceUrl)}resolveNextLink(e){return e.startsWith("http://")||e.startsWith("https://")?e:`${this.serviceUrl}/${e}`}};function xe(t,e){let n=t.trim();if(n.startsWith("{"))try{if("$Version"in JSON.parse(n))return "v4"}catch{}if(n.startsWith("<?xml")||n.startsWith("<edmx:Edmx")||n.startsWith("<Edmx"))return "v2";if(e){if(e.includes("/odata4/"))return "v4";if(e.includes("/odata/"))return "v2"}return "v2"}var P=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.metadataCache=r??new A;}protocolClient=null;version=null;initPromise=null;etagCache=new me;metadataCache;async ensureInitialized(){if(!this.protocolClient)return this.initPromise?this.initPromise:(this.initPromise=this.initialize(),this.initPromise)}async initialize(){let e=this.metadataCache.get(this.serviceUrl);if(e){this.version=e.version,this.protocolClient=this.createProtocolClient(e.version);return}try{let n=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml, application/json"}}),r=typeof n.data=="string"?n.data:JSON.stringify(n.data);this.version=xe(r,this.serviceUrl);}catch{this.version=xe("",this.serviceUrl);}this.protocolClient=this.createProtocolClient(this.version);}createProtocolClient(e){return e==="v4"?new fe(this.httpClient,this.serviceUrl,this.etagCache):new Z(this.httpClient,this.serviceUrl,this.etagCache)}async getClient(){if(await this.ensureInitialized(),!this.protocolClient)throw new Error("ODataClient initialization failed: protocol client not created");return this.protocolClient}async read(e,n,r){return (await this.getClient()).read(e,n,r)}async query(e,n){return (await this.getClient()).query(e,n)}async create(e,n){return (await this.getClient()).create(e,n)}async update(e,n,r){return (await this.getClient()).update(e,n,r)}async delete(e,n){return (await this.getClient()).delete(e,n)}async batch(e,n){let r=await this.getClient();return this.version==="v4"?r.batch(e):r.batch(e,n)}async functionImport(e,n,r){let s=await this.getClient();if(s instanceof Z)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 r=await(await this.getClient()).getMetadata();return this.metadataCache.set(this.serviceUrl,r),r}getVersion(){return this.version}};function St(t,e){let n=new A,r=new ResourceTemplate("sap://schema/{serviceUrl}/{entitySet}",{list:void 0});t.registerResource("sap-entity-schema",r,{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(s,i)=>{let o=decodeURIComponent(i.serviceUrl),a=decodeURIComponent(i.entitySet),p=await new P(e,o,n).getMetadata(),u=p.entitySets.find(f=>f.name===a);if(!u)return {contents:[{uri:s.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity set '${a}' not found in service ${o}`,availableEntitySets:p.entitySets.map(f=>f.name)},null,2)}]};let l=p.entityTypes.find(f=>f.name===u.entityTypeName);if(!l)return {contents:[{uri:s.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity type '${u.entityTypeName}' not found in metadata`},null,2)}]};let d=new Set(l.keys.map(f=>f.name)),m=l.properties.map(f=>({name:f.name,type:f.type,isKey:d.has(f.name)})),g=Gn(l.properties,p.version);return {contents:[{uri:s.toString(),mimeType:"application/json",text:JSON.stringify({entitySet:a,version:p.version,properties:m,sampleFilter:g},null,2)}]}});}function Gn(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 vt(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 Rt(t,e){let n=new A,r=new ResourceTemplate("sap://metadata/{serviceUrl}",{list:void 0});t.registerResource("sap-metadata",r,{title:"SAP Service Metadata",description:"Entity types, properties, keys, and navigation properties for an SAP OData service",mimeType:"application/json"},async(s,i)=>{let o=decodeURIComponent(i.serviceUrl),c=await new P(e,o,n).getMetadata();return {contents:[{uri:s.toString(),mimeType:"application/json",text:JSON.stringify({version:c.version,serviceUrl:c.serviceUrl,entityTypes:c.entityTypes,entitySets:c.entitySets},null,2)}]}});}var ke={"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 Jn={$TODAY:()=>De(new Date),$YESTERDAY:()=>{let t=new Date;return t.setDate(t.getDate()-1),De(t)},$MONTH_START:()=>{let t=new Date;return `${t.getFullYear()}-${Me(t.getMonth()+1)}-01`},$YEAR_START:()=>`${new Date().getFullYear()}-01-01`,$MONTH_AGO:()=>{let t=new Date;return t.setMonth(t.getMonth()-1),De(t)}};function De(t){return `${t.getFullYear()}-${Me(t.getMonth()+1)}-${Me(t.getDate())}`}function Me(t){return t.toString().padStart(2,"0")}function zn(t){if(typeof t!="string")return t;let e=Jn[t];return e?e():t}function Tt(t){return {...t,queryTemplates:t.queryTemplates.map(e=>({...e,filters:e.filters.map(n=>({...n,value:zn(n.value)}))}))}}function _t(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=ke[s];if(!i)return {contents:[{uri:n.toString(),mimeType:"application/json",text:JSON.stringify({error:`Unknown domain '${s}'`,availableDomains:Object.keys(ke)},null,2)}]};let o=Tt(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 Ue={core:["test-connection","sap_list_services","sap_get_metadata","sap_discover_services","sap_nl_query"],odata:["sap_read","sap_query","sap_create","sap_update","sap_delete","sap_function","sap_batch"],idoc:["sap_idoc_send","sap_idoc_status","sap_idoc_discover","sap_idoc_list_received"],rfc:["sap_rfc_call","sap_rfc_metadata","sap_bapi_call","sap_bapi_commit","sap_rfc_search_functions"]},At=new Set(["sap_create","sap_update","sap_delete","sap_function","sap_batch","sap_idoc_send","sap_rfc_call","sap_bapi_call","sap_bapi_commit"]),Ct=["core","odata"],Pt=new Map;for(let[t,e]of Object.entries(Ue))for(let n of e)Pt.set(n,t);var he=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(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=Ue[e],r=[];for(let s of n){if(this.options.readOnly&&At.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)}getToolsByTier(e){return Ue[e]}isReadOnlyMode(){return this.options.readOnly}shouldToolBeVisible(e){let n=Pt.get(e);return n?!(!this.activeTiers.has(n)||n==="idoc"&&!this.options.hasIdocConfig||n==="rfc"&&!this.options.hasRfcConfig||this.options.readOnly&&At.has(e)):true}};function bt(t,e){let n=new A,r=new Map;function s(o){let a=r.get(o);return a||(a=new P(e,o,n),r.set(o,a)),a}function i(o){let a=o instanceof Error?o.message:o&&typeof o=="object"&&"message"in o?String(o.message):"Unknown error";return {content:[{type:"text",text:JSON.stringify({error:a})}],isError:true}}t.registerTool("sap_batch",{title:"Execute SAP Batch Request",description:"Execute multiple OData operations in a single batch request. Supports V2 (multipart/mixed) and V4 (JSON) batch formats automatically. Operations can be grouped into changesets for transactional behavior (all succeed or all fail). Returns per-operation status -- individual failures are always reported, never hidden behind the batch HTTP 200.",inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),operations:z$2.array(z$2.object({method:z$2.enum(["get","post","patch","delete"]).describe("HTTP method"),entitySet:z$2.string().describe("Entity set name"),key:z$2.record(z$2.string(),z$2.unknown()).optional().describe("Entity key for read/update/delete"),data:z$2.record(z$2.string(),z$2.unknown()).optional().describe("Request body for create/update"),changesetId:z$2.string().optional().describe("Changeset group ID for transactional grouping")})).describe("Array of operations to execute in batch")}),annotations:{readOnlyHint:false,destructiveHint:true,idempotentHint:false,openWorldHint:true}},async({serviceUrl:o,operations:a})=>{try{let c=s(o),p=a,u=await c.batch(p);return {content:[{type:"text",text:JSON.stringify(u,null,2)}]}}catch(c){return i(c)}});}var ye=class{cache=null;ttlMs;constructor(e=30){this.ttlMs=e*60*1e3;}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()};}clear(){this.cache=null;}};var Xn=[".integrationsuite.","integrationsuite-trial.",".it-cpi.",".hci.",".prod.apimanagement.","-api.s4hana.ondemand.com"];function Et(t){let e=t.toLowerCase();return Xn.some(n=>e.includes(n))}var O=class extends Error{constructor(n,r,s,i){super(n);this.httpStatus=r;this.sapGuidance=s;this.isBtpDetected=i;this.name="CatalogError";}},Zn=new Set(["ECONNREFUSED","ETIMEDOUT","ENOTFOUND"]);function er(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 tr(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 It(t){return typeof t=="object"&&t!==null&&"response"in t&&typeof t.response=="object"&&t.response!==null&&typeof t.response.status=="number"}function nr(t){return typeof t=="object"&&t!==null&&"code"in t&&typeof t.code=="string"&&Zn.has(t.code)}function ee(t,e,n=false){let r=Et(e);if(It(t)){let i=t.response.status,o=er(i,n),a="message"in t&&typeof t.message=="string"?t.message:`HTTP ${i}`;return new O(a,i,o,r)}if(nr(t)){let i=tr(t.code),o="message"in t&&typeof t.message=="string"?t.message:t.code;return new O(o,void 0,i,r)}let s=t instanceof Error?t.message:String(t);return new O(s,void 0,void 0,r)}function wt(t){return It(t)&&t.response.status===404?"V4 catalog not available. Publish service group via /IWFND/V4_ADMIN.":t instanceof Error?t.message:String(t)}var rr=["/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/iwfnd/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/IWFND/CATALOGSERVICE/ServiceCollection"];function sr(t){if(typeof t=="object"&&t!==null&&"response"in t)return t.response?.status}async function Ot(t,e,n){let r;for(let s of rr)try{return await t.request({method:"get",url:s,params:{$format:"json"}}),n?.onMatch?.(s),s}catch(i){if(sr(i)===404){r=i;continue}throw ee(i,e,false)}throw ee(r??new Error("All V2 catalog paths returned 404"),e,false)}function Nt(t){try{return new URL(t).pathname}catch{return t}}var Se=class t{constructor(e,n,r={}){this.httpClient=e;this.cache=n;this.options=r;}static DEFAULT_V4_URL="/sap/opu/odata4/iwfnd/config/default/iwfnd/catalog/0002/ServiceGroups";resolvedV2Path;async discoverServices(){let e=this.cache.get();if(e)return {services:e,warnings:[]};let n=[],r=[];try{r=await this.fetchV4Catalog();}catch(o){let a=wt(o);n.push(`V4 catalog fetch failed: ${a}`);}let s;try{s=await this.fetchV2Catalog();}catch(o){throw o instanceof O?o:ee(o,this.options.baseUrl??"",false)}let i=this.mergeAndDeduplicate(r,s);return this.cache.set(i),{services:i,warnings:n}}async fetchV2Catalog(){let e=this.options.catalogV2Url??this.resolvedV2Path;return e||(e=await Ot(this.httpClient,this.options.baseUrl??"",{onMatch:this.options.onDiscoveryMatch}),this.resolvedV2Path=e),((await this.httpClient.request({method:"get",url:e,params:{$format:"json"}})).data?.d?.results??[]).map(i=>({technicalName:i.TechnicalServiceName,title:i.Title,serviceUrl:Nt(i.ServiceUrl),description:i.Description,version:"v2",serviceVersion:i.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"}})).data?.value??[],i=[];for(let o of s){let a=o.DefaultSystem?.Services??[];for(let c of a)i.push({technicalName:c.ServiceId??c.ServiceName??"",title:c.ServiceTitle??c.Title??"",serviceUrl:Nt(c.ServiceUrl),version:"v4",serviceVersion:c.ServiceVersion});}return i}mergeAndDeduplicate(e,n){let r=new Map;for(let s of e)r.set(s.technicalName,s);for(let s of n)r.has(s.technicalName)||r.set(s.technicalName,s);return [...r.values()]}};var ir=[[/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"]],or=[[/^[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 ar(t){for(let[e,n]of ir)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};for(let[e,n]of or)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};return {...t,category:"unknown",categoryConfidence:"low"}}function xt(t){return t.map(ar)}function kt(t,e,n){let r=new ye,s=new Se(e,r,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.
|
|
109
|
+
${d}`);}s.push(`--${r}--`);let i=s.join(`\r
|
|
110
|
+
`),o=`multipart/mixed; boundary=${r}`;return {body:i,contentType:o}}var Ln=new Set(["EntityType","Property","NavigationProperty","Association","EntitySet","AssociationSet","PropertyRef","End","FunctionImport","Parameter"]),jn=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",removeNSPrefix:true,isArray:t=>Ln.has(t)});function fe(t,e){let r=jn.parse(t).Edmx.DataServices.Schema,s=Array.isArray(r)?r[0]:r,i=s.EntityType??[],o=s.Association??[],a=s.EntityContainer,c=a?.EntitySet??[],p=new Map;for(let f of o)p.set(f["@_Name"],f);let d=i.map(f=>{let h=Fn(f.Property??[]),R=(f.Key?.PropertyRef??[]).map(O=>O["@_Name"]),S=h.filter(O=>R.includes(O.name)),T=$n(f.NavigationProperty??[],p);return {name:f["@_Name"],keys:S,properties:h,navigationProperties:T}}),l=c.map(f=>({name:f["@_Name"],entityTypeName:xe(f["@_EntityType"])})),u=a?.FunctionImport??[],m=Hn(Array.isArray(u)?u:[u]),g={version:"v2",serviceUrl:e,entityTypes:d,entitySets:l};return m.length>0&&(g.functionImports=m),g}function Fn(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 $n(t,e){return t.map(n=>{let r=xe(n["@_Relationship"]),s=e.get(r),i="Unknown",o=false;if(s){let a=s.End.find(c=>c["@_Role"]===n["@_ToRole"]);a&&(i=xe(a["@_Type"]),o=a["@_Multiplicity"]==="*");}return {name:n["@_Name"],targetEntityType:i,isCollection:o}})}function Hn(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 xe(t){let e=t.lastIndexOf(".");return e>=0?t.substring(e+1):t}var Vn=/^\/Date\((-?\d+)([+-]\d{4})?\)\/$/;function Z(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 dt(t){let e=Vn.exec(t);if(!e)return t;let n=Number(e[1]);return new Date(n).toISOString()}function L(t){if(t==null)return t;if(Array.isArray(t))return t.map(e=>L(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]=dt(s):n[r]=L(s)));return n}return typeof t=="string"?dt(t):t}var ut=10,ee=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;}async read(e,n,r){let s=Ne(r??{});if(n){let i=`${this.serviceUrl}/${H(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=Z(o.data);return {...a,results:a.results.map(c=>L(c))}}return this.fetchWithPagination(`${this.serviceUrl}/${e}`,s,r?.maxPages??ut)}async query(e,n){let r=Ne(n,true),s=n.maxPages??ut;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=Z(r.data);return s.results.length>0?L(s.results[0]):{}}async update(e,n,r){let s=`${this.serviceUrl}/${H(e,n)}`,i=this.etagCache.get(e,n)??"*",o=await this.httpClient.request({method:"post",url:s,data:r,headers:{Accept:"application/json","Content-Type":"application/json","X-HTTP-Method":"MERGE","If-Match":i}});o.headers.etag&&this.etagCache.set(e,n,o.headers.etag);}async delete(e,n){let r=`${this.serviceUrl}/${H(e,n)}`,s=this.etagCache.get(e,n)??"*";await this.httpClient.request({method:"delete",url:r,headers:{Accept:"application/json","If-Match":s}}),this.etagCache.update(e,n,void 0);}async functionImport(e,n,r){let s=(r??"POST").toLowerCase(),i={$format:"json"};if(n)for(let[p,d]of Object.entries(n))typeof d=="string"?i[p]=`'${d}'`:i[p]=String(d);let o=await this.httpClient.request({method:s,url:`${this.serviceUrl}/${e}`,headers:{Accept:"application/json"},params:i}),a=Z(o.data);if(a.results.length>0)return L(a.results[0]);let c=o.data;return c?.d&&typeof c.d=="object"?c.d:{}}async batch(e,n){let{body:r,contentType:s}=lt(this.serviceUrl,e,n),i=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:r,headers:{"Content-Type":s,Accept:"multipart/mixed"}}),o=i.headers["content-type"]??s,a=ot(i.data,o),c=[...e,...(n??[]).flatMap(p=>p.operations)];for(let p=0;p<a.length&&p<c.length;p++)a[p].entitySet=c[p].entitySet;return {results:a}}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml"}});return fe(e.data,this.serviceUrl)}async fetchWithPagination(e,n,r){let s=[],i,o=e,a=n,c=0;for(;o&&c<r;){let l=(await this.httpClient.request({method:"get",url:o,headers:{Accept:"application/json"},params:a})).data,u=Z(l),m=u.results.map(f=>L(f));s.push(...m),c===0&&u.count!==void 0&&(i=u.count),c++;let g=l?.d;g&&typeof g.__next=="string"?(o=g.__next,a=void 0):o=void 0;}let p={results:s};return i!==void 0&&(p.count=i),o&&(p.nextLink=o),p}};function mt(t){let n=(typeof t=="string"?JSON.parse(t):t).responses;return !n||!Array.isArray(n)?[]:n.map(r=>{let s=r.status,i=s>=200&&s<300,o={status:s,entitySet:""};return r.body!==void 0&&r.body!==null&&(i?o.result=r.body:o.error=JSON.stringify(r.body)),o})}function gt(t){return typeof t=="string"?`'${t}'`:String(t)}function k(t,e){let n=Object.entries(e);if(n.length===1){let[,s]=n[0];return `${t}(${gt(s)})`}let r=n.map(([s,i])=>`${s}=${gt(i)}`).join(",");return `${t}(${r})`}function he(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 ft(t,e){let n=[];for(let s=0;s<e.length;s++){let i=e[s],o=i.key?k(i.entitySet,i.key):i.entitySet,a={id:String(s+1),method:i.method.toUpperCase(),url:o};i.changesetId&&(a.atomicityGroup=i.changesetId),i.data&&(i.method==="post"||i.method==="patch"||i.method==="delete")&&(a.headers={"Content-Type":"application/json"},a.body=i.data),n.push(a);}return {body:JSON.stringify({requests:n}),contentType:"application/json"}}function ht(t){let e=t.split(".");return e[e.length-1]}function Bn(t,e){return {name:t,type:e.$Type??"Edm.String",nullable:e.$Nullable??false,maxLength:e.$MaxLength,precision:e.$Precision,scale:e.$Scale}}function yt(t,e){let n=JSON.parse(t),r=[],s=[],i=[];for(let[a,c]of Object.entries(n)){if(a.startsWith("$"))continue;let p=c;for(let[d,l]of Object.entries(p)){let u=l;if(u.$Kind==="Action"||u.$Kind==="Function"){let m=u.$IsBound??false,g=u.$Parameter??[],f=m&&g.length>0?g[0].$Name:void 0,h=(m?g.slice(1):g).map(S=>({name:S.$Name,type:S.$Type??"Edm.String",...S.$Nullable===true?{nullable:true}:{}})),R=u.$ReturnType?u.$ReturnType.$Type:void 0;i.push({name:d,isBound:m,...f?{bindingParameter:f}:{},parameters:h,...R?{returnType:R}:{}});}else if(u.$Kind==="EntityType"){let m=[],g=[];for(let[R,S]of Object.entries(u)){if(R.startsWith("$"))continue;let T=S;T.$Kind==="NavigationProperty"?g.push({name:R,targetEntityType:ht(T.$Type),isCollection:T.$Collection??false}):m.push(Bn(R,T));}let h=(u.$Key??[]).map(R=>{let S=m.find(T=>T.name===R);return S||{name:R,type:"Edm.String",nullable:false}});r.push({name:d,keys:h,properties:m,navigationProperties:g});}else if(u.$Kind==="EntityContainer")for(let[m,g]of Object.entries(u)){if(m.startsWith("$"))continue;let f=g;f.$Collection&&f.$Type&&s.push({name:m,entityTypeName:ht(f.$Type)});}}}let o={version:"v4",serviceUrl:e,entityTypes:r,entitySets:s};return i.length>0&&(o.actions=i),o}var qn=10,ye=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;}async read(e,n,r){if(n){let p=`${this.serviceUrl}/${k(e,n)}`,d=r?he(r):void 0,l=await this.httpClient.request({method:"get",url:p,params:d}),u=l.data,m=l.headers.etag??l.headers.ETag;return m&&this.etagCache.set(e,n,m),{results:[u]}}let s=r?he(r):void 0,o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=o.value??[],c=o["@odata.count"];return {results:a,count:c}}async query(e,n){let r=n.maxPages??qn,s=he(n,true),o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=[...o.value??[]],c=o["@odata.count"],p=o["@odata.nextLink"],d=1;for(;p&&d<r;){let u=this.resolveNextLink(p),g=(await this.httpClient.request({method:"get",url:u})).data,f=g.value??[];a.push(...f),p=g["@odata.nextLink"],d++;}let l={results:a,count:c};return p&&(l.nextLink=p),l}async create(e,n){return (await this.httpClient.request({method:"post",url:`${this.serviceUrl}/${e}`,data:n,headers:{"Content-Type":"application/json"}})).data}async update(e,n,r){let s=`${this.serviceUrl}/${k(e,n)}`,i=this.etagCache.get(e,n)??"*",o=await this.httpClient.request({method:"patch",url:s,data:r,headers:{"Content-Type":"application/json","If-Match":i}}),a=o.headers.etag??o.headers.ETag;a&&this.etagCache.update(e,n,a);}async delete(e,n){let r=`${this.serviceUrl}/${k(e,n)}`,s=this.etagCache.get(e,n)??"*";await this.httpClient.request({method:"delete",url:r,headers:{"If-Match":s}});}async batch(e){let{body:n,contentType:r}=ft(this.serviceUrl,e),s=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:n,headers:{"Content-Type":r,Accept:"application/json"}}),i=mt(s.data);for(let o=0;o<i.length&&o<e.length;o++)i[o].entitySet=e[o].entitySet;return {results:i}}async callAction(e,n,r,s){let i;return r&&s?i=`${this.serviceUrl}/${k(r,s)}/${e}`:i=`${this.serviceUrl}/${e}`,(await this.httpClient.request({method:"post",url:i,data:n??{},headers:{"Content-Type":"application/json"}})).data??{}}async callFunction(e,n,r,s){let i=this.buildFunctionParams(n),o;return r&&s?o=`${this.serviceUrl}/${k(r,s)}/${e}(${i})`:o=`${this.serviceUrl}/${e}(${i})`,(await this.httpClient.request({method:"get",url:o})).data??{}}buildFunctionParams(e){return !e||Object.keys(e).length===0?"":Object.entries(e).map(([n,r])=>typeof r=="string"?`${n}='${r}'`:`${n}=${String(r)}`).join(",")}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/json"}}),n=typeof e.data=="string"?e.data:JSON.stringify(e.data);return yt(n,this.serviceUrl)}resolveNextLink(e){return e.startsWith("http://")||e.startsWith("https://")?e:`${this.serviceUrl}/${e}`}};function ke(t,e){let n=t.trim();if(n.startsWith("{"))try{if("$Version"in JSON.parse(n))return "v4"}catch{}if(n.startsWith("<?xml")||n.startsWith("<edmx:Edmx")||n.startsWith("<Edmx"))return "v2";if(e){if(e.includes("/odata4/"))return "v4";if(e.includes("/odata/"))return "v2"}return "v2"}var P=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.metadataCache=r??new A;}protocolClient=null;version=null;initPromise=null;etagCache=new ge;metadataCache;async ensureInitialized(){if(!this.protocolClient)return this.initPromise?this.initPromise:(this.initPromise=this.initialize(),this.initPromise)}async initialize(){let e=this.metadataCache.get(this.serviceUrl);if(e){this.version=e.version,this.protocolClient=this.createProtocolClient(e.version);return}let n="",r=false;try{let s=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml, application/json"}});n=typeof s.data=="string"?s.data:JSON.stringify(s.data),this.version=ke(n,this.serviceUrl),r=!0;}catch{this.version=ke("",this.serviceUrl);}if(this.protocolClient=this.createProtocolClient(this.version),r&&this.version==="v2"&&n)try{let s=fe(n,this.serviceUrl);this.metadataCache.set(this.serviceUrl,s);}catch{}}createProtocolClient(e){return e==="v4"?new ye(this.httpClient,this.serviceUrl,this.etagCache):new ee(this.httpClient,this.serviceUrl,this.etagCache)}async getClient(){if(await this.ensureInitialized(),!this.protocolClient)throw new Error("ODataClient initialization failed: protocol client not created");return this.protocolClient}async read(e,n,r){return (await this.getClient()).read(e,n,r)}async query(e,n){return (await this.getClient()).query(e,n)}async create(e,n){return (await this.getClient()).create(e,n)}async update(e,n,r){return (await this.getClient()).update(e,n,r)}async delete(e,n){return (await this.getClient()).delete(e,n)}async batch(e,n){let r=await this.getClient();return this.version==="v4"?r.batch(e):r.batch(e,n)}async functionImport(e,n,r){let s=await this.getClient();if(s instanceof ee)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 St(t,e,n){let r=n??new A,s=new ResourceTemplate("sap://schema/{serviceUrl}/{entitySet}",{list:void 0});t.registerResource("sap-entity-schema",s,{title:"SAP Entity Schema for Query Building",description:"Property names, types, and keys for an entity set \u2014 optimized context for building OData queries. Includes a sample filter in the correct V2/V4 syntax.",mimeType:"application/json"},async(i,o)=>{let a=decodeURIComponent(o.serviceUrl),c=decodeURIComponent(o.entitySet),d=await new P(e,a,r).getMetadata(),l=d.entitySets.find(h=>h.name===c);if(!l)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity set '${c}' not found in service ${a}`,availableEntitySets:d.entitySets.map(h=>h.name)},null,2)}]};let u=d.entityTypes.find(h=>h.name===l.entityTypeName);if(!u)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity type '${l.entityTypeName}' not found in metadata`},null,2)}]};let m=new Set(u.keys.map(h=>h.name)),g=u.properties.map(h=>({name:h.name,type:h.type,isKey:m.has(h.name)})),f=Gn(u.properties,d.version);return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({entitySet:c,version:d.version,properties:g,sampleFilter:f},null,2)}]}});}function Gn(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 vt(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 Rt(t,e,n){let r=n??new A,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),p=await new P(e,a,r).getMetadata();return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({version:p.version,serviceUrl:p.serviceUrl,entityTypes:p.entityTypes,entitySets:p.entitySets},null,2)}]}});}var De={"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 Jn={$TODAY:()=>Me(new Date),$YESTERDAY:()=>{let t=new Date;return t.setDate(t.getDate()-1),Me(t)},$MONTH_START:()=>{let t=new Date;return `${t.getFullYear()}-${Ue(t.getMonth()+1)}-01`},$YEAR_START:()=>`${new Date().getFullYear()}-01-01`,$MONTH_AGO:()=>{let t=new Date;return t.setMonth(t.getMonth()-1),Me(t)}};function Me(t){return `${t.getFullYear()}-${Ue(t.getMonth()+1)}-${Ue(t.getDate())}`}function Ue(t){return t.toString().padStart(2,"0")}function zn(t){if(typeof t!="string")return t;let e=Jn[t];return e?e():t}function Tt(t){return {...t,queryTemplates:t.queryTemplates.map(e=>({...e,filters:e.filters.map(n=>({...n,value:zn(n.value)}))}))}}function _t(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=De[s];if(!i)return {contents:[{uri:n.toString(),mimeType:"application/json",text:JSON.stringify({error:`Unknown domain '${s}'`,availableDomains:Object.keys(De)},null,2)}]};let o=Tt(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 Le={core:["test-connection","sap_list_services","sap_get_metadata","sap_discover_services","sap_nl_query"],odata:["sap_read","sap_query","sap_create","sap_update","sap_delete","sap_function","sap_batch"],idoc:["sap_idoc_send","sap_idoc_status","sap_idoc_discover","sap_idoc_list_received"],rfc:["sap_rfc_call","sap_rfc_metadata","sap_bapi_call","sap_bapi_commit","sap_rfc_search_functions"]},Ct=new Set(["sap_create","sap_update","sap_delete","sap_function","sap_batch","sap_idoc_send","sap_rfc_call","sap_bapi_call","sap_bapi_commit"]),At=["core","odata"],Pt=new Map;for(let[t,e]of Object.entries(Le))for(let n of e)Pt.set(n,t);var Se=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(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=Le[e],r=[];for(let s of n){if(this.options.readOnly&&Ct.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)}getToolsByTier(e){return Le[e]}isReadOnlyMode(){return this.options.readOnly}shouldToolBeVisible(e){let n=Pt.get(e);return n?!(!this.activeTiers.has(n)||n==="idoc"&&!this.options.hasIdocConfig||n==="rfc"&&!this.options.hasRfcConfig||this.options.readOnly&&Ct.has(e)):true}};function bt(t,e,n){let r=n??new A,s=new Map;function i(a){let c=s.get(a);return c||(c=new P(e,a,r),s.set(a,c)),c}function o(a){let c=a instanceof Error?a.message:a&&typeof a=="object"&&"message"in a?String(a.message):"Unknown error";return {content:[{type:"text",text:JSON.stringify({error:c})}],isError:true}}t.registerTool("sap_batch",{title:"Execute SAP Batch Request",description:"Execute multiple OData operations in a single batch request. Supports V2 (multipart/mixed) and V4 (JSON) batch formats automatically. Operations can be grouped into changesets for transactional behavior (all succeed or all fail). Returns per-operation status -- individual failures are always reported, never hidden behind the batch HTTP 200.",inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),operations:z$2.array(z$2.object({method:z$2.enum(["get","post","patch","delete"]).describe("HTTP method"),entitySet:z$2.string().describe("Entity set name"),key:z$2.record(z$2.string(),z$2.unknown()).optional().describe("Entity key for read/update/delete"),data:z$2.record(z$2.string(),z$2.unknown()).optional().describe("Request body for create/update"),changesetId:z$2.string().optional().describe("Changeset group ID for transactional grouping")})).describe("Array of operations to execute in batch")}),annotations:{readOnlyHint:false,destructiveHint:true,idempotentHint:false,openWorldHint:true}},async({serviceUrl:a,operations:c})=>{try{let p=i(a),d=c,l=await p.batch(d);return {content:[{type:"text",text:JSON.stringify(l,null,2)}]}}catch(p){return o(p)}});}var V=class{cache=null;ttlMs;constructor(e=30){this.ttlMs=e*60*1e3;}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()};}clear(){this.cache=null;}};var Xn=[".integrationsuite.","integrationsuite-trial.",".it-cpi.",".hci.",".prod.apimanagement.","-api.s4hana.ondemand.com"];function Et(t){let e=t.toLowerCase();return Xn.some(n=>e.includes(n))}var N=class extends Error{constructor(n,r,s,i){super(n);this.httpStatus=r;this.sapGuidance=s;this.isBtpDetected=i;this.name="CatalogError";}},Zn=new Set(["ECONNREFUSED","ETIMEDOUT","ENOTFOUND"]);function er(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 tr(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 It(t){return typeof t=="object"&&t!==null&&"response"in t&&typeof t.response=="object"&&t.response!==null&&typeof t.response.status=="number"}function nr(t){return typeof t=="object"&&t!==null&&"code"in t&&typeof t.code=="string"&&Zn.has(t.code)}function te(t,e,n=false){let r=Et(e);if(It(t)){let i=t.response.status,o=er(i,n),a="message"in t&&typeof t.message=="string"?t.message:`HTTP ${i}`;return new N(a,i,o,r)}if(nr(t)){let i=tr(t.code),o="message"in t&&typeof t.message=="string"?t.message:t.code;return new N(o,void 0,i,r)}let s=t instanceof Error?t.message:String(t);return new N(s,void 0,void 0,r)}function wt(t){return It(t)&&t.response.status===404?"V4 catalog not available. Publish service group via /IWFND/V4_ADMIN.":t instanceof Error?t.message:String(t)}var rr=["/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/iwfnd/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/IWFND/CATALOGSERVICE/ServiceCollection"];function sr(t){if(typeof t=="object"&&t!==null&&"response"in t)return t.response?.status}async function Ot(t,e,n){let r;for(let s of rr)try{return await t.request({method:"get",url:s,params:{$format:"json"}}),n?.onMatch?.(s),s}catch(i){if(sr(i)===404){r=i;continue}throw te(i,e,false)}throw te(r??new Error("All V2 catalog paths returned 404"),e,false)}function Nt(t){try{return new URL(t).pathname}catch{return t}}var ve=class t{constructor(e,n,r={}){this.httpClient=e;this.cache=n;this.options=r;}static DEFAULT_V4_URL="/sap/opu/odata4/iwfnd/config/default/iwfnd/catalog/0002/ServiceGroups";resolvedV2Path;async discoverServices(){let e=this.cache.get();if(e)return {services:e,warnings:[]};let n=[],r=[];try{r=await this.fetchV4Catalog();}catch(o){let a=wt(o);n.push(`V4 catalog fetch failed: ${a}`);}let s;try{s=await this.fetchV2Catalog();}catch(o){throw o instanceof N?o:te(o,this.options.baseUrl??"",false)}let i=this.mergeAndDeduplicate(r,s);return this.cache.set(i),{services:i,warnings:n}}async fetchV2Catalog(){let e=this.options.catalogV2Url??this.resolvedV2Path;return e||(e=await Ot(this.httpClient,this.options.baseUrl??"",{onMatch:this.options.onDiscoveryMatch}),this.resolvedV2Path=e),((await this.httpClient.request({method:"get",url:e,params:{$format:"json"}})).data?.d?.results??[]).map(i=>({technicalName:i.TechnicalServiceName,title:i.Title,serviceUrl:Nt(i.ServiceUrl),description:i.Description,version:"v2",serviceVersion:i.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"}})).data?.value??[],i=[];for(let o of s){let a=o.DefaultSystem?.Services??[];for(let c of a)i.push({technicalName:c.ServiceId??c.ServiceName??"",title:c.ServiceTitle??c.Title??"",serviceUrl:Nt(c.ServiceUrl),version:"v4",serviceVersion:c.ServiceVersion});}return i}mergeAndDeduplicate(e,n){let r=new Map;for(let s of e)r.set(s.technicalName,s);for(let s of n)r.has(s.technicalName)||r.set(s.technicalName,s);return [...r.values()]}};var ir=[[/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"]],or=[[/^[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 ar(t){for(let[e,n]of ir)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};for(let[e,n]of or)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};return {...t,category:"unknown",categoryConfidence:"low"}}function xt(t){return t.map(ar)}function kt(t,e,n,r){let s=r??new V,i=new ve(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.
|
|
111
111
|
|
|
112
112
|
**When to use:**
|
|
113
113
|
- You need to find which SAP services are available on this system
|
|
@@ -116,7 +116,7 @@ ${u}`);}s.push(`--${r}--`);let i=s.join(`\r
|
|
|
116
116
|
|
|
117
117
|
**When NOT to use:**
|
|
118
118
|
- You already know the service URL (proceed to sap_query or sap_nl_query)
|
|
119
|
-
- 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)"),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:
|
|
119
|
+
- 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)"),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:c,offset:p})=>{try{let d=c??50,l=p??0,u=await i.discoverServices(),m=xt(u.services);if(a&&(m=m.filter(S=>S.category===a)),o){let S=o.toLowerCase();m=m.filter(T=>T.technicalName.toLowerCase().includes(S)||T.title.toLowerCase().includes(S)||(T.description?.toLowerCase().includes(S)??!1));}let g=m.length,f={};for(let S of m)f[S.category]=(f[S.category]??0)+1;let h=m.slice(l,l+d),R={services:h.map(S=>({technicalName:S.technicalName,title:S.title,category:S.category,serviceUrl:S.serviceUrl,version:S.version})),summary:{total:g,returned:h.length,offset:l,byCategory:f},...u.warnings.length>0?{warnings:u.warnings}:{}};return {content:[{type:"text",text:JSON.stringify(R,null,2)}]}}catch(d){if(d instanceof N){let u={error:d.message};return d.sapGuidance&&(u.guidance=d.sapGuidance),d.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}}let l=d instanceof Error?d.message:"Unknown error";return {content:[{type:"text",text:JSON.stringify({error:l})}],isError:true}}});}function Dt(t,e,n){let r=new A;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.
|
|
120
120
|
|
|
121
121
|
**When to use:**
|
|
122
122
|
- You need the raw list of OData services at a base URL
|
|
@@ -132,7 +132,7 @@ ${u}`);}s.push(`--${r}--`);let i=s.join(`\r
|
|
|
132
132
|
- Use 'search' to filter large services by name pattern
|
|
133
133
|
|
|
134
134
|
**When NOT to use:**
|
|
135
|
-
- 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 c=await new P(e,s,r).getMetadata(),p;if(i){let u=c.entityTypes.find(l=>l.name===i);if(!u){let l=i.toLowerCase();u=c.entityTypes.find(d=>d.name.toLowerCase()===l);}if(!u){let l=i.toLowerCase(),d=c.entityTypes.filter(m=>m.name.toLowerCase().includes(l)).map(m=>m.name).slice(0,20);return {content:[{type:"text",text:JSON.stringify({error:`Entity type '${i}' not found`,hint:"Use search parameter to find matching types, or omit entityType to see all entity sets.",closestMatches:d.length>0?d:void 0})}],isError:!0}}p={version:c.version,entityType:u};}else {let u=c.entitySets.filter(m=>M(m.name,n)),l=c.entityTypes;if(o){let m=o.toLowerCase();u=u.filter(g=>g.name.toLowerCase().includes(m)),l=l.filter(g=>g.name.toLowerCase().includes(m));}l.length>50&&!o?p={version:c.version,entitySets:u,entityTypeCount:l.length,hint:`Service has ${l.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.`}:p={version:c.version,entityTypes:l.map(m=>({name:m.name,keys:m.keys.map(g=>g.name),propertyCount:m.properties.length,navigationProperties:m.navigationProperties.map(g=>g.name)})),entitySets:u};}return {content:[{type:"text",text:JSON.stringify(p,null,2)}]}}catch(a){let c=a instanceof Error?a.message:"Unknown error";return {content:[{type:"text",text:JSON.stringify({error:c})}],isError:true}}});}function Mt(t,e){let n=new A,r=new Map;function s(o){let a=r.get(o);return a||(a=new P(e,o,n),r.set(o,a)),a}function i(o){let a=o instanceof Error?o.message:o&&typeof o=="object"&&"message"in o?String(o.message):"Unknown error";return {content:[{type:"text",text:JSON.stringify({error:a})}],isError:true}}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. For V4: supports bound actions (on a specific entity) and unbound actions/functions. Set isFunction=true for V4 Functions (GET), leave false for V4 Actions (POST). For bound operations, provide entitySet and key.",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:o,functionName:a,parameters:c,httpMethod:p,entitySet:u,key:l,isFunction:d})=>{try{let g=await s(o).functionImport(a,c,{httpMethod:p,entitySet:u,key:l,isFunction:d??!1});return {content:[{type:"text",text:JSON.stringify(g,null,2)}]}}catch(m){return i(m)}});}function Ut(t,e){t.registerTool("test-connection",{title:"Test SAP Connection",description:"Test connectivity to the configured SAP system by sending a simple request",inputSchema:z$2.object({}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async()=>{try{return await e.request({method:"get",url:"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/",headers:{Accept:"application/json"}}),{content:[{type:"text",text:JSON.stringify({status:"connected",timestamp:new Date().toISOString(),hint:"Use sap_list_services with a specific OData service path like /sap/opu/odata/sap/API_BUSINESS_PARTNER to discover entity sets."})}]}}catch(n){let r=n&&typeof n=="object"&&"message"in n?n.message:"Unknown error";return {content:[{type:"text",text:JSON.stringify({status:"error",message:r,timestamp:new Date().toISOString()})}],isError:true}}});}function pr(t){return {name:t.FieldName,dataType:t.DataType,length:t.Length,decimals:t.Decimals??0,required:t.Required}}function lr(t){return {name:t.SegmentName,parentSegment:t.ParentSegment??"",minOccurrence:t.MinOccurrence,maxOccurrence:t.MaxOccurrence,fields:t.Fields?.results?.map(pr)??[]}}function dr(t){return {idocType:t.IdocType,mesType:t.MesType,segments:t.Segments?.results?.map(lr)??[]}}async function Le(t,e,n){try{if(n){let o=(await t.request({method:"get",url:`${e}/IdocTypes('${n}')?$expand=Segments($expand=Fields)`})).data;return dr(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 Lt={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 jt(t,e,n){try{let i=(await t.request({method:"get",url:`${e}/IdocStatus('${n}')`})).data.d,o=i.Status,a=Lt[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 je={name:z$2.string().describe("Segment name, e.g. E1MARAM"),fields:z$2.record(z$2.string(),z$2.string()).describe("Segment field values")},ur=z$2.object({...je}),mr=z$2.object({...je,children:z$2.array(ur).optional().describe("Nested child segments (level 3)")}),gr=z$2.object({...je,children:z$2.array(mr).optional().describe("Nested child segments")});function Ft(t,e,n,r){function s(i){let o=i instanceof Error?i.message:i&&typeof i=="object"&&"message"in i?String(i.message):"Unknown error";return {content:[{type:"text",text:JSON.stringify({error:o})}],isError:true}}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(gr).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:c,sndpor:p,sndprn:u,rcvpor:l,rcvprn:d})=>{try{let m;if(a){let f=await Le(e,a,i);Array.isArray(f)||(m=f);}let g=await n.send({idocType:i,segments:o,metadata:m,partnerOverrides:{mesType:c,sndpor:p,sndprn:u,rcvpor:l,rcvprn:d}});return {content:[{type:"text",text:JSON.stringify(g,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 jt(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 Le(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 fr={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 Ht(t,e){if(e.isReadOnlyMode())return;let n=e.getActiveTiers(),s=["core","odata","idoc","rfc"].filter(o=>!n.includes(o)),i=s.length>0?`Available tiers to activate: ${s.map(o=>`${o} (${fr[o]})`).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(", ")}. ${i}`,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:o})=>{let a=e.enableTier(o);return "error"in a?{content:[{type:"text",text:JSON.stringify({status:"error",message:a.error})}],isError:true}:{content:[{type:"text",text:JSON.stringify({status:"success",tier_enabled:o,enabled_tools:a.enabled,active_tiers:a.activeTiers,note:"Tools enabled. If your client did not refresh the tool list automatically, you may need to reconnect."})}]}});}function Vt(t,e){return t.length===0?"":t.map(n=>hr(n,e)).join(" and ")}function hr(t,e){let{field:n,operator:r,value:s}=t;switch(r){case "contains":return yr(n,String(s),e);case "startswith":return `startswith(${n},'${ve(String(s))}')`;case "endswith":return `endswith(${n},'${ve(String(s))}')`;default:return Sr(n,r,s,e)}}function yr(t,e,n){let r=ve(e);return n==="v2"?`substringof('${r}',${t})`:`contains(${t},'${r}')`}function Sr(t,e,n,r){let s=vr(n,r);return `${t} ${e} ${s}`}function vr(t,e){return typeof t=="number"||typeof t=="boolean"?String(t):Rr(t)?Tr(t,e):`'${ve(t)}'`}function Rr(t){return /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?)?/.test(t)}function Tr(t,e){return e==="v2"?`datetime'${t.includes("T")?t:`${t}T00:00:00`}'`:t}function ve(t){return t.replace(/'/g,"''")}function Bt(t,e,n){let r=new A,s=new Map;function i(a){let c=s.get(a);return c||(c=new P(e,a,r),s.set(a,c)),c}function o(a){let c=a instanceof Error?a.message:a&&typeof a=="object"&&"message"in a?String(a.message):"Unknown error";return {content:[{type:"text",text:JSON.stringify({error:c})}],isError:true}}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.
|
|
135
|
+
- 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 c=await new P(e,s,r).getMetadata(),p;if(i){let d=c.entityTypes.find(l=>l.name===i);if(!d){let l=i.toLowerCase();d=c.entityTypes.find(u=>u.name.toLowerCase()===l);}if(!d){let l=i.toLowerCase(),u=c.entityTypes.filter(m=>m.name.toLowerCase().includes(l)).map(m=>m.name).slice(0,20);return {content:[{type:"text",text:JSON.stringify({error:`Entity type '${i}' not found`,hint:"Use search parameter to find matching types, or omit entityType to see all entity sets.",closestMatches:u.length>0?u:void 0})}],isError:!0}}p={version:c.version,entityType:d};}else {let d=c.entitySets.filter(m=>M(m.name,n)),l=c.entityTypes;if(o){let m=o.toLowerCase();d=d.filter(g=>g.name.toLowerCase().includes(m)),l=l.filter(g=>g.name.toLowerCase().includes(m));}l.length>50&&!o?p={version:c.version,entitySets:d,entityTypeCount:l.length,hint:`Service has ${l.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.`}:p={version:c.version,entityTypes:l.map(m=>({name:m.name,keys:m.keys.map(g=>g.name),propertyCount:m.properties.length,navigationProperties:m.navigationProperties.map(g=>g.name)})),entitySets:d};}return {content:[{type:"text",text:JSON.stringify(p,null,2)}]}}catch(a){let c=a instanceof Error?a.message:"Unknown error";return {content:[{type:"text",text:JSON.stringify({error:c})}],isError:true}}});}function Mt(t,e,n){let r=n??new A,s=new Map;function i(a){let c=s.get(a);return c||(c=new P(e,a,r),s.set(a,c)),c}function o(a){let c=a instanceof Error?a.message:a&&typeof a=="object"&&"message"in a?String(a.message):"Unknown error";return {content:[{type:"text",text:JSON.stringify({error:c})}],isError:true}}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. For V4: supports bound actions (on a specific entity) and unbound actions/functions. Set isFunction=true for V4 Functions (GET), leave false for V4 Actions (POST). For bound operations, provide entitySet and key.",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:a,functionName:c,parameters:p,httpMethod:d,entitySet:l,key:u,isFunction:m})=>{try{let f=await i(a).functionImport(c,p,{httpMethod:d,entitySet:l,key:u,isFunction:m??!1});return {content:[{type:"text",text:JSON.stringify(f,null,2)}]}}catch(g){return o(g)}});}function Ut(t,e){t.registerTool("test-connection",{title:"Test SAP Connection",description:"Test connectivity to the configured SAP system by sending a simple request",inputSchema:z$2.object({}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async()=>{try{return await e.request({method:"get",url:"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/",headers:{Accept:"application/json"}}),{content:[{type:"text",text:JSON.stringify({status:"connected",timestamp:new Date().toISOString(),hint:"Use sap_list_services with a specific OData service path like /sap/opu/odata/sap/API_BUSINESS_PARTNER to discover entity sets."})}]}}catch(n){let r=n&&typeof n=="object"&&"message"in n?n.message:"Unknown error";return {content:[{type:"text",text:JSON.stringify({status:"error",message:r,timestamp:new Date().toISOString()})}],isError:true}}});}function pr(t){return {name:t.FieldName,dataType:t.DataType,length:t.Length,decimals:t.Decimals??0,required:t.Required}}function lr(t){return {name:t.SegmentName,parentSegment:t.ParentSegment??"",minOccurrence:t.MinOccurrence,maxOccurrence:t.MaxOccurrence,fields:t.Fields?.results?.map(pr)??[]}}function dr(t){return {idocType:t.IdocType,mesType:t.MesType,segments:t.Segments?.results?.map(lr)??[]}}async function je(t,e,n){try{if(n){let o=(await t.request({method:"get",url:`${e}/IdocTypes('${n}')?$expand=Segments($expand=Fields)`})).data;return dr(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 Lt={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 jt(t,e,n){try{let i=(await t.request({method:"get",url:`${e}/IdocStatus('${n}')`})).data.d,o=i.Status,a=Lt[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 Fe={name:z$2.string().describe("Segment name, e.g. E1MARAM"),fields:z$2.record(z$2.string(),z$2.string()).describe("Segment field values")},ur=z$2.object({...Fe}),mr=z$2.object({...Fe,children:z$2.array(ur).optional().describe("Nested child segments (level 3)")}),gr=z$2.object({...Fe,children:z$2.array(mr).optional().describe("Nested child segments")});function Ft(t,e,n,r){function s(i){let o=i instanceof Error?i.message:i&&typeof i=="object"&&"message"in i?String(i.message):"Unknown error";return {content:[{type:"text",text:JSON.stringify({error:o})}],isError:true}}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(gr).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:c,sndpor:p,sndprn:d,rcvpor:l,rcvprn:u})=>{try{let m;if(a){let f=await je(e,a,i);Array.isArray(f)||(m=f);}let g=await n.send({idocType:i,segments:o,metadata:m,partnerOverrides:{mesType:c,sndpor:p,sndprn:d,rcvpor:l,rcvprn:u}});return {content:[{type:"text",text:JSON.stringify(g,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 jt(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 je(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 fr={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 Ht(t,e){if(e.isReadOnlyMode())return;let n=e.getActiveTiers(),s=["core","odata","idoc","rfc"].filter(o=>!n.includes(o)),i=s.length>0?`Available tiers to activate: ${s.map(o=>`${o} (${fr[o]})`).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(", ")}. ${i}`,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:o})=>{let a=e.enableTier(o);return "error"in a?{content:[{type:"text",text:JSON.stringify({status:"error",message:a.error})}],isError:true}:{content:[{type:"text",text:JSON.stringify({status:"success",tier_enabled:o,enabled_tools:a.enabled,active_tiers:a.activeTiers,note:"Tools enabled. If your client did not refresh the tool list automatically, you may need to reconnect."})}]}});}function Vt(t,e){return t.length===0?"":t.map(n=>hr(n,e)).join(" and ")}function hr(t,e){let{field:n,operator:r,value:s}=t;switch(r){case "contains":return yr(n,String(s),e);case "startswith":return `startswith(${n},'${Re(String(s))}')`;case "endswith":return `endswith(${n},'${Re(String(s))}')`;default:return Sr(n,r,s,e)}}function yr(t,e,n){let r=Re(e);return n==="v2"?`substringof('${r}',${t})`:`contains(${t},'${r}')`}function Sr(t,e,n,r){let s=vr(n,r);return `${t} ${e} ${s}`}function vr(t,e){return typeof t=="number"||typeof t=="boolean"?String(t):Rr(t)?Tr(t,e):`'${Re(t)}'`}function Rr(t){return /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?)?/.test(t)}function Tr(t,e){return e==="v2"?`datetime'${t.includes("T")?t:`${t}T00:00:00`}'`:t}function Re(t){return t.replace(/'/g,"''")}function Bt(t,e,n,r){let s=r??new A,i=new Map;function o(c){let p=i.get(c);return p||(p=new P(e,c,s),i.set(c,p)),p}function a(c){let p=c instanceof Error?c.message:c&&typeof c=="object"&&"message"in c?String(c.message):"Unknown error";return {content:[{type:"text",text:JSON.stringify({error:p})}],isError:true}}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.
|
|
136
136
|
|
|
137
137
|
**When to use:**
|
|
138
138
|
- You have a natural language question about SAP data
|
|
@@ -142,7 +142,7 @@ ${u}`);}s.push(`--${r}--`);let i=s.join(`\r
|
|
|
142
142
|
**When NOT to use:**
|
|
143
143
|
- You need to create, update, or delete data (use sap_create/sap_update/sap_delete)
|
|
144
144
|
- You already have an exact OData $filter string (use sap_query instead)
|
|
145
|
-
- 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:
|
|
145
|
+
- 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:c,entitySet:p,filters:d,select:l,orderby:u,top:m,skip:g})=>{if(!M(p,n))return a(new Error(`Entity set '${p}' is not exposed. Configure --expose to include it.`));try{let f=o(c),R=(await f.getMetadata()).version,S=Vt(d,R),T=await f.query(p,{filter:S||void 0,select:l,orderby:u,top:m,skip:g});return {content:[{type:"text",text:JSON.stringify({version:R,generatedFilter:S||"(none)",results:T.results,count:T.count,nextLink:T.nextLink},null,2)}]}}catch(f){return a(f)}});}function qt(t,e,n,r){let s=r??new A,i=new Map;function o(p){let d=i.get(p);return d||(d=new P(e,p,s),i.set(p,d)),d}function a(p){let d=p instanceof Error?p.message:p&&typeof p=="object"&&"message"in p?String(p.message):"Unknown error";return {content:[{type:"text",text:JSON.stringify({error:d})}],isError:true}}function c(p){return M(p,n)?null:a(new Error(`Entity set '${p}' 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.
|
|
146
146
|
|
|
147
147
|
**When to use:**
|
|
148
148
|
- You have a specific entity key and want one record
|
|
@@ -150,7 +150,7 @@ ${u}`);}s.push(`--${r}--`);let i=s.join(`\r
|
|
|
150
150
|
|
|
151
151
|
**When NOT to use:**
|
|
152
152
|
- You want to list/filter multiple entities (use sap_query)
|
|
153
|
-
- You don't know the entity key (query first, then read)`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set name, e.g., A_BusinessPartner"),key:z$2.record(z$2.string(),z$2.unknown()).optional().describe('Entity key as object, e.g., { BusinessPartner: "1000001" }'),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")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:
|
|
153
|
+
- You don't know the entity key (query first, then read)`,inputSchema:z$2.object({serviceUrl:z$2.string().describe("OData service URL"),entitySet:z$2.string().describe("Entity set name, e.g., A_BusinessPartner"),key:z$2.record(z$2.string(),z$2.unknown()).optional().describe('Entity key as object, e.g., { BusinessPartner: "1000001" }'),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")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:p,entitySet:d,key:l,select:u,expand:m})=>{let g=c(d);if(g)return g;try{let f=o(p),h={select:u,expand:m},R=await f.read(d,l,h);return {content:[{type:"text",text:JSON.stringify(R,null,2)}]}}catch(f){return a(f)}}),t.registerTool("sap_query",{title:"Query SAP Entities",description:`Query SAP entities with filter, sort, pagination. Returns matching entities with count. Per D-03: each query option is a separate typed parameter.
|
|
154
154
|
|
|
155
155
|
**When to use:**
|
|
156
156
|
- You have an exact OData $filter string ready
|
|
@@ -160,7 +160,7 @@ ${u}`);}s.push(`--${r}--`);let i=s.join(`\r
|
|
|
160
160
|
**When NOT to use:**
|
|
161
161
|
- You have a natural language question (use sap_nl_query instead)
|
|
162
162
|
- You don't know which service to query (use sap://routing/{domain} first, then sap_discover_services)
|
|
163
|
-
- 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 per page"),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:
|
|
163
|
+
- 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 per page"),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:p,entitySet:d,filter:l,select:u,expand:m,orderby:g,top:f,skip:h,maxPages:R})=>{let S=c(d);if(S)return S;try{let O=await o(p).query(d,{filter:l,select:u,expand:m,orderby:g,top:f,skip:h,maxPages:R});return {content:[{type:"text",text:JSON.stringify(O,null,2)}]}}catch(T){return a(T)}}),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:p,entitySet:d,data:l})=>{let u;try{u=JSON.parse(l);}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=c(d);if(m)return m;try{let f=await o(p).create(d,u);return {content:[{type:"text",text:JSON.stringify({created:f},null,2)}]}}catch(g){return a(g)}}),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"}'`),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:p,entitySet:d,key:l,data:u})=>{let m,g;try{m=JSON.parse(l),g=JSON.parse(u);}catch{return a(new Error("Invalid JSON in key or data parameter"))}let f=c(d);if(f)return f;try{return await o(p).update(d,m,g),{content:[{type:"text",text:JSON.stringify({updated:!0,entitySet:d,key:l})}]}}catch(h){return a(h)}}),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"}'`)}),annotations:{readOnlyHint:false,destructiveHint:true,idempotentHint:true,openWorldHint:true}},async({serviceUrl:p,entitySet:d,key:l})=>{let u;try{u=JSON.parse(l);}catch{return a(new Error("Invalid JSON in key parameter"))}let m=c(d);if(m)return m;try{return await o(p).delete(d,u),{content:[{type:"text",text:JSON.stringify({deleted:!0,entitySet:d,key:l})}]}}catch(g){return a(g)}});}function re(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=>_r(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 _r(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:x(t.MESSAGE_V1),messageV2:x(t.MESSAGE_V2),messageV3:x(t.MESSAGE_V3),messageV4:x(t.MESSAGE_V4),parameter:x(t.PARAMETER),row:Cr(t.ROW),field:x(t.FIELD),system:x(t.SYSTEM),logNo:x(t.LOG_NO),logMsgNo:x(t.LOG_MSG_NO)}}function x(t){if(t==null)return;let e=String(t);return e===""?void 0:e}function Cr(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 Wt(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 Te=null;async function Gt(t){if(Te)return true;try{return Te=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 Kt(){if(!Te)throw new Error("node-rfc not loaded \u2014 call tryLoadRfc() first");return Te}var Yt=6e4,Pr=5e3,br=2,Er=10,q=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";}},_e=class{pool;sessions=new Map;logger;closed=false;constructor(e,n){let s=Kt().Pool;this.pool=new s({connectionParameters:{...e},poolOptions:{low:br,high:Er}}),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 q(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 q(e);clearTimeout(r.idleTimer);try{let s=n?{WAIT:"X"}:{},i=await r.client.call("BAPI_TRANSACTION_COMMIT",s),{hasErrors:o}=re(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()+Pr;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),Yt)};return this.sessions.set(n,r),r}refreshIdle(e){clearTimeout(e.idleTimer),e.idleTimer=setTimeout(()=>this.onIdle(e.id),Yt);}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 se(t){if(t instanceof q)return {content:[{type:"text",text:JSON.stringify({code:"SESSION_NOT_FOUND",message:t.message,sessionId:t.sessionId})}],isError:true};let e=Wt(t);return {content:[{type:"text",text:JSON.stringify(e)}],isError:true}}var Ir={I:"IMPORT",E:"EXPORT",C:"CHANGING",T:"TABLES",X:"EXCEPTION"};function Jt(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.
|
|
164
164
|
|
|
165
165
|
When to use:
|
|
166
166
|
- Custom Z_* function modules (no BAPI commit needed)
|
|
@@ -170,7 +170,7 @@ When NOT to use:
|
|
|
170
170
|
- For BAPIs that need transactional commit -> use sap_bapi_call + sap_bapi_commit instead
|
|
171
171
|
- sap_rfc_call is STATELESS: it CANNOT participate in BAPI commit transactions. If you need BAPI_TRANSACTION_COMMIT after this call, the function module must be invoked via sap_bapi_call.
|
|
172
172
|
|
|
173
|
-
The function module must be flagged Remote-Enabled in SE37. Returns the raw output parameters from node-rfc (UPPERCASE keys). SAP date/time/decimal types come as strings (e.g. '20260427', '143052', '1234.56') \u2014 see README for type-mapping notes.`,inputSchema:z$2.object({functionModule:z$2.string().min(1).describe("Function module name, e.g. RFC_PING, RFC_READ_TABLE, Z_CUSTOM_FM"),parameters:z$2.record(z$2.string(),z$2.unknown()).optional().describe('IMPORT/CHANGING/TABLES parameters as ABAP-uppercase keys, e.g. { QUERY_TABLE: "T001", DELIMITER: "|" }')}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:false,openWorldHint:true}},async({functionModule:r,parameters:s})=>{try{let i=await e.callOnce(r.toUpperCase(),s??{});return {content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return
|
|
173
|
+
The function module must be flagged Remote-Enabled in SE37. Returns the raw output parameters from node-rfc (UPPERCASE keys). SAP date/time/decimal types come as strings (e.g. '20260427', '143052', '1234.56') \u2014 see README for type-mapping notes.`,inputSchema:z$2.object({functionModule:z$2.string().min(1).describe("Function module name, e.g. RFC_PING, RFC_READ_TABLE, Z_CUSTOM_FM"),parameters:z$2.record(z$2.string(),z$2.unknown()).optional().describe('IMPORT/CHANGING/TABLES parameters as ABAP-uppercase keys, e.g. { QUERY_TABLE: "T001", DELIMITER: "|" }')}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:false,openWorldHint:true}},async({functionModule:r,parameters:s})=>{try{let i=await e.callOnce(r.toUpperCase(),s??{});return {content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return se(i)}}),t.registerTool("sap_rfc_metadata",{title:"Get SAP RFC Function Module Interface",description:`Retrieve the IMPORT/EXPORT/CHANGING/TABLES/EXCEPTIONS interface of an RFC-enabled function module (via RFC_GET_FUNCTION_INTERFACE).
|
|
174
174
|
|
|
175
175
|
When to use:
|
|
176
176
|
- Before calling an unfamiliar RFC/BAPI to understand its parameters
|
|
@@ -179,7 +179,7 @@ When to use:
|
|
|
179
179
|
When NOT to use:
|
|
180
180
|
- To search for function modules by name pattern -> use sap_rfc_search_functions
|
|
181
181
|
|
|
182
|
-
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),p=(a.PARAMS??[]).map(l=>{let
|
|
182
|
+
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),p=(a.PARAMS??[]).map(l=>{let u=String(l.PARAMCLASS??"").trim(),m=Ir[u]??"IMPORT",g=String(l.TABNAME??"").trim(),f=String(l.FIELDNAME??"").trim(),h=g&&f?`${g}-${f}`:g||f||void 0,R=String(l.DECIMALS??"").trim(),S=R===""?void 0:Number.parseInt(R,10),T=Number.isNaN(S)?void 0:S,O=String(l.INTLENGTH??"0").trim(),W=Number.parseInt(O===""?"0":O,10)||0,j=String(l.DEFAULT??"").trim(),E=String(l.PARAMTEXT??"").trim();return {name:String(l.PARAMETER??"").trim(),direction:m,type:String(l.EXID??"").trim(),length:W,decimals:T,optional:String(l.OPTIONAL??"").trim()==="X",default:j===""?void 0:j,text:E===""?void 0:E,ddicReference:h}}),d={name:i,remoteEnabled:String(a.REMOTE_CALL??"")==="X",updateTask:String(a.UPDATE_TASK??"")==="X",parameters:p};return {content:[{type:"text",text:JSON.stringify(d,null,2)}]}}catch(i){return se(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.
|
|
183
183
|
|
|
184
184
|
When to use:
|
|
185
185
|
- BAPI_USER_CREATE1, BAPI_SALESORDER_CREATEFROMDAT2, BAPI_*_CHANGE, etc.
|
|
@@ -192,7 +192,7 @@ Critical behavior:
|
|
|
192
192
|
- The output contains a parsed \`return\` array (BAPIRET2 messages, camelCase) plus \`hasErrors\`, \`errorCount\`, \`warningCount\`.
|
|
193
193
|
- If hasErrors=true, do NOT call sap_bapi_commit. The session has not yet been committed; either call sap_bapi_call again with corrected params (sessionId reused) or let the session idle out (auto-rollback after 60s).
|
|
194
194
|
- hasErrors=true is NOT isError=true at the MCP transport level (the call itself succeeded).
|
|
195
|
-
- If sessionId is omitted, a fresh stateful connection is opened. Pass it back to chain BAPIs on the same connection before BAPI_TRANSACTION_COMMIT.`,inputSchema:z$2.object({bapi:z$2.string().min(1).describe("BAPI function module name, e.g. BAPI_USER_GET_DETAIL"),parameters:z$2.record(z$2.string(),z$2.unknown()).optional().describe("BAPI input parameters with ABAP-uppercase keys"),sessionId:z$2.string().uuid().optional().describe("Continue an existing session from a previous sap_bapi_call. Omit on first call.")}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:false,openWorldHint:true}},async({bapi:r,parameters:s,sessionId:i})=>{try{let{result:o,sessionId:a}=await e.callBapi(r.toUpperCase(),s??{},i),c=
|
|
195
|
+
- If sessionId is omitted, a fresh stateful connection is opened. Pass it back to chain BAPIs on the same connection before BAPI_TRANSACTION_COMMIT.`,inputSchema:z$2.object({bapi:z$2.string().min(1).describe("BAPI function module name, e.g. BAPI_USER_GET_DETAIL"),parameters:z$2.record(z$2.string(),z$2.unknown()).optional().describe("BAPI input parameters with ABAP-uppercase keys"),sessionId:z$2.string().uuid().optional().describe("Continue an existing session from a previous sap_bapi_call. Omit on first call.")}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:false,openWorldHint:true}},async({bapi:r,parameters:s,sessionId:i})=>{try{let{result:o,sessionId:a}=await e.callBapi(r.toUpperCase(),s??{},i),c=re(o),{RETURN:p,E_RETURN:d,...l}=o,u={},m={};for(let[g,f]of Object.entries(l))Array.isArray(f)?m[g]=f:u[g]=f;return {content:[{type:"text",text:JSON.stringify({sessionId:a,return:c.messages,hasErrors:c.hasErrors,errorCount:c.errorCount,warningCount:c.warningCount,exports:u,tables:m},null,2)}]}}catch(o){return se(o)}}),t.registerTool("sap_bapi_commit",{title:"Commit SAP BAPI Transaction",description:`Call BAPI_TRANSACTION_COMMIT on the SAME stateful connection used by a prior sap_bapi_call. Releases the session afterwards.
|
|
196
196
|
|
|
197
197
|
When to use:
|
|
198
198
|
- After one or more sap_bapi_call invocations that returned hasErrors=false
|
|
@@ -201,7 +201,7 @@ When NOT to use:
|
|
|
201
201
|
- If the prior sap_bapi_call returned hasErrors=true (commit would persist a faulty state \u2014 implicit rollback by letting the session idle is safer)
|
|
202
202
|
- For non-BAPI RFC calls (sap_rfc_call cannot participate)
|
|
203
203
|
|
|
204
|
-
Returns committed=true when BAPI_TRANSACTION_COMMIT returns no E/A messages.`,inputSchema:z$2.object({sessionId:z$2.string().uuid().describe("Session id from a prior sap_bapi_call"),wait:z$2.boolean().optional().default(true).describe("When true (default), waits for ABAP UPDATE-Task to complete (WAIT=X). When false, fire-and-forget (risky \u2014 failures only visible in SM58/SM37).")}),annotations:{readOnlyHint:false,destructiveHint:true,idempotentHint:false,openWorldHint:true}},async({sessionId:r,wait:s})=>{try{let{result:i,committed:o,sessionId:a}=await e.commit(r,s??!0),c=
|
|
204
|
+
Returns committed=true when BAPI_TRANSACTION_COMMIT returns no E/A messages.`,inputSchema:z$2.object({sessionId:z$2.string().uuid().describe("Session id from a prior sap_bapi_call"),wait:z$2.boolean().optional().default(true).describe("When true (default), waits for ABAP UPDATE-Task to complete (WAIT=X). When false, fire-and-forget (risky \u2014 failures only visible in SM58/SM37).")}),annotations:{readOnlyHint:false,destructiveHint:true,idempotentHint:false,openWorldHint:true}},async({sessionId:r,wait:s})=>{try{let{result:i,committed:o,sessionId:a}=await e.commit(r,s??!0),c=re(i);return {content:[{type:"text",text:JSON.stringify({sessionId:a,return:c.messages,committed:o},null,2)}]}}catch(i){return se(i)}}),t.registerTool("sap_rfc_search_functions",{title:"Search SAP RFC Function Modules",description:`Search for function modules by name pattern via RFC_FUNCTION_SEARCH.
|
|
205
205
|
|
|
206
206
|
When to use:
|
|
207
207
|
- Discover BAPIs/FMs by partial name (e.g. "BAPI_USER_*", "Z_*")
|
|
@@ -212,7 +212,7 @@ When NOT to use:
|
|
|
212
212
|
Wildcards (SAP-native, NOT glob):
|
|
213
213
|
- \`*\` matches any sequence of characters
|
|
214
214
|
- \`+\` matches a single character
|
|
215
|
-
Patterns are case-folded to uppercase. Default limit is 100; set higher to widen, but SAP returns unpaginated results \u2014 large patterns may stress the backend.`,inputSchema:z$2.object({pattern:z$2.string().min(1).describe('Name pattern with SAP wildcards (* and +). Examples: "BAPI_USER_*", "Z_*", "+MATERIAL+"'),groupName:z$2.string().optional().describe('Optional function group filter (RS38L_AREA, e.g. "SU05")'),limit:z$2.number().int().positive().optional().default(100).describe("Max number of hits to return (default 100). Excess is truncated.")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({pattern:r,groupName:s,limit:i})=>{try{let o={FUNCNAME:r.toUpperCase()};s&&(o.GROUPNAME=s.toUpperCase());let c=(await e.callOnce("RFC_FUNCTION_SEARCH",o)).FUNCTIONS??[],p=c.length,
|
|
215
|
+
Patterns are case-folded to uppercase. Default limit is 100; set higher to widen, but SAP returns unpaginated results \u2014 large patterns may stress the backend.`,inputSchema:z$2.object({pattern:z$2.string().min(1).describe('Name pattern with SAP wildcards (* and +). Examples: "BAPI_USER_*", "Z_*", "+MATERIAL+"'),groupName:z$2.string().optional().describe('Optional function group filter (RS38L_AREA, e.g. "SU05")'),limit:z$2.number().int().positive().optional().default(100).describe("Max number of hits to return (default 100). Excess is truncated.")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({pattern:r,groupName:s,limit:i})=>{try{let o={FUNCNAME:r.toUpperCase()};s&&(o.GROUPNAME=s.toUpperCase());let c=(await e.callOnce("RFC_FUNCTION_SEARCH",o)).FUNCTIONS??[],p=c.length,d=i??100,l=p>d,g={functions:(l?c.slice(0,d):c).map(f=>{let h=String(f.TEXT??"").trim();return {name:String(f.FUNCNAME??"").trim(),groupName:String(f.GROUPNAME??"").trim(),shortText:h===""?void 0:h}}),total:p,...l?{truncated:!0}:{}};return {content:[{type:"text",text:JSON.stringify(g,null,2)}]}}catch(o){return se(o)}});}function ie(t,e){let n=new McpServer({name:"guniweb-sap-mcp",version:"1.1.0"},{capabilities:{logging:{},resources:{},prompts:{},tools:{listChanged:true}}}),r={readOnly:e?.readOnly??false,activeTiers:e?.tiers??At,hasIdocConfig:e?.hasIdocConfig??false,hasRfcConfig:e?.hasRfcConfig??false},s=new Se(r),i=n.registerTool.bind(n);n.registerTool=((l,...u)=>{let m=i(l,...u);return s.registerHandle(l,m),m});let o=Je(t),a=new le(o,e?.httpOptions);e?.userContext&&(a.userContext=e.userContext);let c=e?.exposePatterns;st(n),rt(n),Ut(n,a),Dt(n,a,c),kt(n,a,{catalogV2Url:e?.catalogV2Url,catalogV4Url:e?.catalogV4Url,baseUrl:t.baseUrl,onDiscoveryMatch:e?.onDiscoveryMatch},e?.caches?.catalog),qt(n,a,c,e?.caches?.metadata),Mt(n,a,e?.caches?.metadata),bt(n,a,e?.caches?.metadata),Bt(n,a,c,e?.caches?.metadata);let p=e?.idocStore??new F,d=new de(a,e?.sapClient,e?.partnerDefaults);return Ft(n,a,d,p),vt(n,p),e?.rfcAvailable&&e.rfcPool&&e.logger&&Jt(n,e.rfcPool,e.logger),Ht(n,s),Rt(n,a,e?.caches?.metadata),St(n,a,e?.caches?.metadata),_t(n),s.applyInitialVisibility(),{server:n,registry:s,httpClient:a}}var Or=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 $e(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=Or.safeParse({snc_qop:t.SAP_SNC_QOP,snc_myname:t.SAP_SNC_MYNAME,snc_partnername:t.SAP_SNC_PARTNERNAME,snc_lib:t.SAP_SNC_LIB});if(!o.success){let a=o.error.issues.map(c=>c.message).join("; ");throw new Error(`Invalid SNC configuration: ${a}`)}return o.data.snc_qop&&Object.assign(i,o.data),i}function zt(t=process.env){try{return $e(t)!==null}catch{return false}}function He(t){let e=t.authorization??t.Authorization;if(typeof e!="string"||!e.startsWith("Bearer "))return null;let n=e.slice(7);if(!n)return null;try{let r=decodeJwt(n);return typeof r.sub!="string"?null:{token:n,sub:r.sub,email:typeof r.email=="string"?r.email:void 0,name:typeof r.name=="string"?r.name:void 0}}catch{return null}}async function Qt(t){let e=createMcpExpressApp({host:"0.0.0.0"});if(t.apiKey){let s=`Bearer ${t.apiKey}`;e.use((i,o,a)=>{let c=i,p=o,d=a;if(c.path==="/healthz"){d();return}if(c.headers.authorization!==s){p.status(401).json({error:"Unauthorized"});return}d();});}e.get("/healthz",(s,i)=>{i.status(200).json({status:"ok"});}),t.transport==="http"?Ur(e,t):Lr(e,t);let n=ae(t.idocStore);e.post("/idoc",(s,i)=>{n(s,i);});let r=createServer(e);return new Promise(s=>{r.listen(t.port,()=>{let i=r.address(),o=typeof i=="object"&&i!==null?i.port:t.port;s({port:o,close:()=>new Promise((a,c)=>{r.close(p=>p?c(p):a());})});});})}function Ur(t,e){let n=new V,r=new A;t.all("/mcp",(s,i)=>{let o=s,a=i,c=new StreamableHTTPServerTransport({sessionIdGenerator:void 0}),p=He(o.headers),{server:d}=ie(e.config,{...e.serverOptions,userContext:p,caches:{catalog:n,metadata:r}});d.connect(c).then(()=>c.handleRequest(o,a,o.body)).catch(()=>{a.headersSent||(a.writeHead(500),a.end("Internal Server Error"));});});}function Lr(t,e){let n=new Map;t.get("/sse",(r,s)=>{let i=s,o=new SSEServerTransport("/message",i),{server:a,httpClient:c}=ie(e.config,e.serverOptions);a.connect(o).then(()=>o.start()).then(()=>{n.set(o.sessionId,{transport:o,server:a,httpClient:c}),o.onclose=()=>{n.delete(o.sessionId);};}).catch(()=>{i.headersSent||(i.writeHead(500),i.end("Internal Server Error"));});}),t.post("/message",(r,s)=>{let i=r,o=s,c=new URL(i.url??"",`http://${i.headers.host??"localhost"}`).searchParams.get("sessionId")??i.query?.sessionId;if(!c||!n.has(c)){o.writeHead(400,{"Content-Type":"application/json"}),o.end(JSON.stringify({error:"Invalid or missing session ID"}));return}let p=n.get(c),d=He(i.headers);p.httpClient.userContext=d,p.transport.handlePostMessage(i,o,i.body).catch(()=>{o.headersSent||(o.writeHead(500),o.end("Internal Server Error"));});});}var Hr=`Usage: guniweb-sap-mcp [options]
|
|
216
216
|
|
|
217
217
|
SAP S/4HANA MCP Server -- OData V2/V4 and IDoc integration.
|
|
218
218
|
|
|
@@ -233,5 +233,5 @@ Configuration via environment variables: SAP_BASE_URL, SAP_AUTH_TYPE,
|
|
|
233
233
|
See README for the full list.
|
|
234
234
|
|
|
235
235
|
Repository: https://github.com/jhaertf/guniweb-sap-mcp
|
|
236
|
-
`;async function Vr(){let t=
|
|
236
|
+
`;async function Vr(){let t=Ve();t.help&&(process.stdout.write(Hr),process.exit(0));let e=We(),n=Ce(process.env),r=In({transport:{target:"pino/file",options:{destination:2}}});n.tlsVerify===false&&r.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)."),n.clientCert&&r.info({hasCert:true,hasPassphrase:!!n.clientKeyPassphrase},"X.509 client certificate enabled for OData transport"),r.info({baseUrl:e.baseUrl,authType:e.authType},"SAP MCP server starting"),t.expose&&r.info({exposePatterns:t.expose},"Entity set filtering active");let s=await Gt(r),i;if(s){let u=$e(process.env);if(u){u.snc_lib&&!existsSync(u.snc_lib)&&r.warn({path:u.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."),i=new _e(u,r);let m=!!u.snc_qop;r.info({ashost:u.ashost,mshost:u.mshost,snc:m,...m?{qop:u.snc_qop,myname:u.snc_myname}:{}},m?"RFC pool ready (SNC-secured)":"RFC pool ready");}else r.info("node-rfc loaded but no SAP_RFC_* config \u2014 RFC tier requires sap_enable_tools rfc + env vars to activate");}let o=new F,a={exposePatterns:t.expose,idocStore:o,sapClient:e.sapClient,partnerDefaults:{sndpor:t.sndpor,sndprt:t.sndprt,sndprn:t.sndprn,rcvpor:t.rcvpor,rcvprt:t.rcvprt,rcvprn:t.rcvprn},catalogV2Url:t.catalogV2Url,catalogV4Url:t.catalogV4Url,httpOptions:n,onDiscoveryMatch:u=>{r.info({path:u,attemptedPaths:3},"V2 catalog auto-discovery: matched path");}},c=t.transport??"stdio",p={...a,readOnly:t.readOnly,tiers:t.tiers,hasIdocConfig:Be(t),hasRfcConfig:zt(process.env),rfcAvailable:s,rfcPool:i,logger:r},d;if(c==="stdio"){let{server:u}=ie(e,p),m=new StdioServerTransport;if(await u.connect(m),r.info("SAP MCP server connected on stdio transport"),t.webhookPort){let g=await Ye(t.webhookPort,o);r.info({port:g.port},"IDoc webhook server listening");}}else {let u=t.port??8808;d=await Qt({transport:c,port:u,apiKey:t.apiKey,config:e,serverOptions:p,idocStore:o}),r.info({transport:c,port:d.port,auth:t.apiKey?"api-key":"open"},"SAP MCP server listening on HTTP");}let l=async u=>{r.info({signal:u},"Shutting down...");try{i&&await i.closeAll(),d&&await d.close();}catch(m){r.error({err:m},"Shutdown error (continuing)");}process.exit(0);};process.on("SIGTERM",()=>l("SIGTERM")),process.on("SIGINT",()=>l("SIGINT"));}Vr().catch(t=>{process.stderr.write(`Fatal error: ${t instanceof Error?t.message:String(t)}
|
|
237
237
|
`),process.exit(1);});
|