guniweb-sap-mcp 0.3.0 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +36 -16
  2. package/dist/index.js +35 -34
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # GuniWeb SAP S/4HANA & ECC MCP Server
2
2
 
3
- Designed for n8n, usable from any MCP host
3
+ Designed for n8n, usable from any MCP host. Free to use — built and maintained by [GuniWeb](https://guniweb.de).
4
4
 
5
- Connect n8n workflows to SAP S/4HANA and ECC via OData V2, V4, IDocs, and RFC/BAPI using the Model Context Protocol (MCP).
5
+ Connect n8n workflows to SAP S/4HANA and ECC via OData V2, V4, IDocs, and RFC/BAPI using the Model Context Protocol (MCP). Made for **AI-assisted, human-governed workflows**: the workflow author decides which SAP APIs are called and in what order; the LLM extracts, maps and fills in parameters. Writes are off by default and switched on explicitly, per server and per token.
6
6
 
7
7
  ```mermaid
8
8
  graph LR
9
- n8n["n8n AI Agent<br/>+ LLM"] -->|MCP Protocol| MCP["guniweb-sap-mcp<br/>22 Tools &bull; 7 Auth Types"]
9
+ n8n["n8n workflow<br/>AI-assisted, human-governed"] -->|MCP Protocol| MCP["guniweb-sap-mcp<br/>22 Tools &bull; 7 Auth Types"]
10
10
  MCP -->|"OData V2/V4<br/>IDoc XML<br/>RFC/BAPI"| SAP["SAP S/4HANA & ECC"]
11
11
  MCP -->|"OAuth2 / JWT"| BTP["SAP BTP"]
12
12
  BTP --> SAP
@@ -33,12 +33,13 @@ graph LR
33
33
  - **Smart Query Routing** -- Domain-based routing resources for LLM tool selection
34
34
  - **Decision-Tree Tool Descriptions** -- "When to use / When NOT to use" blocks guide LLMs
35
35
 
36
- ### Tool Visibility (v1.5)
37
- - **3-Tier Tool Registry** -- Core / OData / IDoc tiers with on-demand activation
38
- - **`sap_enable_tools`** -- LLMs activate additional tool tiers at runtime
36
+ ### Governed tool visibility
37
+ - **4-Tier Tool Registry** -- Core / OData / IDoc / RFC tiers with on-demand activation
38
+ - **`sap_enable_tools`** -- LLMs activate additional tool tiers at runtime, within the limits you set
39
39
  - **Read-only by default (since 0.2.2)** -- write tools require `--allow-write`
40
- - **IDoc Auto-Disable** -- IDoc tools hidden when no IDoc config present
41
- - **MCP Annotations** -- readOnlyHint, destructiveHint, idempotentHint on all 17 tools
40
+ - **Per-token policy (0.3.0)** -- `readOnly` and tool-tier ceilings per Bearer token, only ever more restrictive
41
+ - **IDoc / RFC auto-disable** -- tools hidden when the corresponding configuration is missing
42
+ - **MCP Annotations** -- readOnlyHint, destructiveHint, idempotentHint on all 22 tools
42
43
 
43
44
  ### Authentication (7 Types)
44
45
 
@@ -65,8 +66,9 @@ graph TB
65
66
 
66
67
  ### Infrastructure
67
68
  - **Three Transport Modes** -- stdio, Streamable HTTP, SSE
68
- - **API Key Protection** -- Secure HTTP transport with Bearer token
69
+ - **Named destinations (0.3.0)** -- one server, several SAP systems; a Bearer token per n8n credential selects the system and its permissions. Static API key still supported
69
70
  - **Docker Ready** -- Docker Compose for n8n + SAP MCP sidecar
71
+ - **Zero telemetry** -- the server talks to the SAP/BTP endpoints you configure and to nothing else. No usage statistics, no update checks, no crash reports
70
72
  - **1267 Tests** -- Unit, integration, E2E with CI/CD via GitHub Actions
71
73
 
72
74
  ## Quick Start
@@ -235,8 +237,8 @@ guniweb-sap-mcp destinations remove s4test [--force] # refuses while token
235
237
  ### 4. Use with n8n
236
238
 
237
239
  1. Add an **AI Agent** node with an **MCP Client Tool** sub-node
238
- 2. Set the MCP Client URL to `http://sap-mcp:8808/mcp` (Docker) or `http://localhost:8808/mcp`
239
- 3. The AI agent discovers SAP tools automatically and can query, create, update SAP data
240
+ 2. Set the MCP Client URL to `http://sap-mcp:8808/mcp` (Docker) or `http://localhost:8808/mcp`, with a Bearer Auth credential holding your API key or destination token
241
+ 3. The AI Agent node discovers the SAP tools automatically. **Which** tools it sees is your decision, not the model's: read-only by default, write tools only with `--allow-write`, and per-token policies narrow it further. Design the workflow as a deterministic, human-authored sequence — the LLM fills in the parameters
240
242
 
241
243
  ## Setup for SAP ECC
242
244
 
@@ -246,7 +248,7 @@ ECC NetWeaver backends require additional setup beyond the [Quick Start](#quick-
246
248
 
247
249
  In transaction `SICF`, activate the standard ICF services under `/sap/opu/odata/`. Then use transaction `/IWFND/MAINT_SERVICE` to register the OData services you want to expose.
248
250
 
249
- The MCP server's catalog auto-discovery (Phase 23) probes three paths in sequence and caches the first 200 hit:
251
+ The MCP server's catalog auto-discovery probes three paths in sequence and caches the first 200 hit:
250
252
 
251
253
  - `/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/ServiceCollection` (S/4HANA default)
252
254
  - `/sap/opu/odata/iwfnd/CATALOGSERVICE;v=2/ServiceCollection` (lower-case ECC)
@@ -473,10 +475,28 @@ Additionally validated against:
473
475
 
474
476
  ## Documentation
475
477
 
476
- - [Setup Guide](docs/setup-guide.md) -- Installation, all 7 auth types, CLI flags, Docker deployment
477
- - [Architecture](docs/architecture.md) -- Technical architecture, request flows, design decisions
478
- - [API Reference](docs/api-reference.md) -- All 17 tools, resources, and prompts with parameters
479
- - [Examples](docs/examples.md) -- SAP workflow examples with step-by-step instructions
478
+ - [Setup Guide](https://github.com/guniweb/guniweb-sap-mcp/blob/main/docs/setup-guide.md) -- Installation, all 7 auth types, CLI flags, named destinations and tokens, Docker deployment
479
+ - [Architecture](https://github.com/guniweb/guniweb-sap-mcp/blob/main/docs/architecture.md) -- Technical architecture, request flows, design decisions
480
+ - [API Reference](https://github.com/guniweb/guniweb-sap-mcp/blob/main/docs/api-reference.md) -- All 22 tools, resources, and prompts with parameters
481
+ - [Examples](https://github.com/guniweb/guniweb-sap-mcp/blob/main/docs/examples.md) -- SAP workflow examples with step-by-step instructions
482
+ - [SAP interfaces, tool by tool](https://github.com/guniweb/guniweb-sap-mcp/blob/main/docs/sap-interfaces.md) -- which SAP endpoint each of the 22 tools calls, what is bounded, what leaves your network — for Basis teams, license managers and auditors
483
+ - [Changelog](https://github.com/guniweb/guniweb-sap-mcp/blob/main/CHANGELOG.md) · [Releases](https://github.com/guniweb/guniweb-sap-mcp/releases) · [Issues](https://github.com/guniweb/guniweb-sap-mcp/issues) · [Discussions](https://github.com/guniweb/guniweb-sap-mcp/discussions)
484
+
485
+ ## Governance: how this fits SAP's rules
486
+
487
+ - **Standard integration interfaces only.** The server speaks OData V2/V4 through NetWeaver Gateway, IDoc over HTTP/XML and RFC/BAPI — the documented, supported ways into an SAP system ([tool-by-tool list](https://github.com/guniweb/guniweb-sap-mcp/blob/main/docs/sap-interfaces.md)). No UI automation, no undocumented interfaces, no bulk replication of SAP data. Paging limits and time budgets are built in so a single tool call cannot turn into a load test of your Gateway.
488
+ - **The write path is governed and auditable.** Read-only by default; write tools exist only with an explicit `--allow-write`; per-token policies can only restrict, never widen; every tool call leaves one log line with a correlation id that also appears on each SAP request it triggered. That is what makes "human-governed" verifiable: the workflow author, not the model, decides where writes are possible.
489
+ - **Two things to settle before you automate document creation.** SAP's API usage policy contains terms on autonomous, generative use of its APIs — build workflows as deterministic, human-authored sequences with LLM assistance rather than open-ended autonomous agents (this server is designed for exactly that). And business documents created automatically (a sales order, for instance) may fall under SAP Digital Access licensing for your tenant — clarify that with your SAP account team before you go live. Neither sentence is legal advice; both are the questions your auditor will ask.
490
+
491
+ ## Who is behind this — and how to get help
492
+
493
+ guniweb-sap-mcp is built and maintained by [GuniWeb](https://guniweb.de) — a consultancy with its own solutions: *processes that work, systems that grow with you*. GuniWeb optimises business processes for mid-sized companies, with more than 20 years of practice, strategic consulting and hands-on implementation, so that a company does not just become more digital but runs better. This server is one of those solutions: it runs in production at customers and is used in teaching and research. The compiled package on npm is **free to use** under the ISC license; the source code is not public.
494
+
495
+ - **Community** — questions, bug reports and ideas via [Issues](https://github.com/guniweb/guniweb-sap-mcp/issues) and [Discussions](https://github.com/guniweb/guniweb-sap-mcp/discussions), best effort. See [SUPPORT.md](https://github.com/guniweb/guniweb-sap-mcp/blob/main/SUPPORT.md).
496
+ - **Production support** — maintenance subscription with prioritised fixes, compatibility with SAP release changes and a direct channel; optionally with a response-time SLA for the write path: **[guniweb.de/sap-mcp](https://guniweb.de/sap-mcp#support)**
497
+ - **SAP MCP Checkup** — we review your n8n/SAP setup (auth, governance, tool visibility, performance) before it goes live: **[guniweb.de/sap-mcp](https://guniweb.de/sap-mcp#checkup)**
498
+
499
+ Contact: [support@guniweb.de](mailto:support@guniweb.de). Security issues: see [SECURITY.md](https://github.com/guniweb/guniweb-sap-mcp/blob/main/SECURITY.md) — and note the zero-telemetry commitment there.
480
500
 
481
501
  ## Technology
482
502
 
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import {readFileSync,existsSync,watchFile,watch,unwatchFile,mkdirSync,writeFileSync,renameSync,unlinkSync}from'fs';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import us from'pino';import {minimatch}from'minimatch';import {z}from'zod/v4';import {lookup}from'dns/promises';import {connect}from'tls';import Ct,{isAxiosError}from'axios';import {AsyncLocalStorage}from'async_hooks';import {randomUUID,timingSafeEqual,randomBytes,createHash}from'crypto';import {jwtVerify,decodeJwt,createRemoteJWKSet}from'jose';import {SignedXml}from'xml-crypto';import {Agent}from'https';import {resolve,join,dirname}from'path';import {z as z$1}from'zod';import {readFile}from'fs/promises';import {createServer}from'http';import {XMLParser,XMLBuilder}from'fast-xml-parser';import {McpServer,ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {createMcpExpressApp}from'@modelcontextprotocol/sdk/server/express.js';import {SSEServerTransport}from'@modelcontextprotocol/sdk/server/sse.js';import {StreamableHTTPServerTransport}from'@modelcontextprotocol/sdk/server/streamableHttp.js';function he(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 Vt(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 Dr(t,e)}function Dr(t,e){let n=t.indexOf("--webhook-port");n!==-1&&n<t.length-1&&(e.webhookPort=Number.parseInt(t[n+1],10));let r=t.indexOf("--webhook-secret");r!==-1&&r<t.length-1&&(e.webhookSecret=t[r+1]),!e.webhookSecret&&process.env.SAP_WEBHOOK_SECRET&&(e.webhookSecret=process.env.SAP_WEBHOOK_SECRET),e.webhookSecret!==void 0&&e.webhookSecret.trim().length===0&&(e.webhookSecret=void 0);let s=t.indexOf("--webhook-host");if(s!==-1&&s<t.length-1&&(e.webhookHost=t[s+1]),!e.webhookHost&&process.env.SAP_WEBHOOK_HOST&&(e.webhookHost=process.env.SAP_WEBHOOK_HOST),process.env.SAP_WEBHOOK_MAX_BODY_BYTES){let C=Number.parseInt(process.env.SAP_WEBHOOK_MAX_BODY_BYTES,10);Number.isFinite(C)&&C>0&&(e.webhookMaxBodyBytes=C);}let i=t.indexOf("--transport");if(i!==-1&&i<t.length-1){let C=t[i+1];(C==="stdio"||C==="http"||C==="sse")&&(e.transport=C);}let o=t.indexOf("--port");o!==-1&&o<t.length-1&&(e.port=Number.parseInt(t[o+1],10));let a=t.indexOf("--api-key");a!==-1&&a<t.length-1&&(e.apiKey=t[a+1]);let c=["sndpor","sndprt","sndprn","rcvpor","rcvprt","rcvprn"];for(let C of c){let q=t.indexOf(`--${C}`);q!==-1&&q<t.length-1&&(e[C]=t[q+1]);}let l=t.indexOf("--catalog-v2-url");l!==-1&&l<t.length-1&&(e.catalogV2Url=t[l+1]),!e.catalogV2Url&&process.env.ODATA_V2_CATALOG_PATH&&(e.catalogV2Url=process.env.ODATA_V2_CATALOG_PATH);let u=t.indexOf("--catalog-v4-url");u!==-1&&u<t.length-1&&(e.catalogV4Url=t[u+1]);let d=t.includes("--allow-write")||process.env.SAP_MCP_ALLOW_WRITE==="true";e.readOnly=!d,e.allowWriteExplicit=d;let m=t.indexOf("--tool-timeout"),p=m!==-1&&m<t.length-1?t[m+1]:process.env.SAP_MCP_TOOL_TIMEOUT;if(p!==void 0){let C=Number.parseInt(p,10);if(!Number.isFinite(C)||C<0)throw new Error(`--tool-timeout muss eine Zahl in Sekunden sein (0 schaltet das Budget ab), war: "${p}"`);e.toolTimeoutSeconds=C;}let f=t.indexOf("--cache-dir"),g=f!==-1&&f<t.length-1?t[f+1]:process.env.SAP_CACHE_DIR;g&&g.trim().length>0&&(e.cacheDir=g.trim());let y=t.indexOf("--concurrency"),h=y!==-1&&y<t.length-1?t[y+1]:process.env.SAP_CONCURRENCY;if(h!==void 0){let C=Number.parseInt(h,10);if(!Number.isFinite(C)||C<1)throw new Error(`--concurrency muss eine Zahl >= 1 sein, war: "${h}"`);e.concurrency=C;}let S=t.indexOf("--destinations"),v=S!==-1&&S<t.length-1?t[S+1]:process.env.SAP_MCP_DESTINATIONS;v&&v.trim().length>0&&(e.destinationsFile=v.trim());let k=t.indexOf("--help"),L=t.indexOf("-h");(k!==-1||L!==-1)&&(e.help=true);let F=t.indexOf("--tiers");if(F!==-1&&F<t.length-1){let C=t[F+1].split(",").map(q=>q.trim()).filter(q=>q.length>0);e.tiers=C.length>0?C:void 0;}let X=t.indexOf("--destination-name");X!==-1&&X<t.length-1&&(e.destinationName=t[X+1]),!e.destinationName&&process.env.SAP_DESTINATION_NAME&&(e.destinationName=process.env.SAP_DESTINATION_NAME);let mt=t.indexOf("--destination-service-url");mt!==-1&&mt<t.length-1&&(e.destinationServiceUrl=t[mt+1]),!e.destinationServiceUrl&&process.env.SAP_DESTINATION_SERVICE_URL&&(e.destinationServiceUrl=process.env.SAP_DESTINATION_SERVICE_URL);let ft=t.indexOf("--issuer");ft!==-1&&ft<t.length-1&&(e.issuer=t[ft+1]),!e.issuer&&process.env.SAP_ISSUER&&(e.issuer=process.env.SAP_ISSUER);let gt=t.indexOf("--name-id-format");gt!==-1&&gt<t.length-1&&(e.nameIdFormat=t[gt+1]),!e.nameIdFormat&&process.env.SAP_NAME_ID_FORMAT&&(e.nameIdFormat=process.env.SAP_NAME_ID_FORMAT);let ht=t.indexOf("--private-key"),kr=ht!==-1&&ht<t.length-1?t[ht+1]:void 0;e.privateKey=he(kr,"SAP_PRIVATE_KEY","SAP_PRIVATE_KEY_FILE");let yt=t.indexOf("--certificate"),Ir=yt!==-1&&yt<t.length-1?t[yt+1]:void 0;e.certificate=he(Ir,"SAP_CERTIFICATE","SAP_CERTIFICATE_FILE");let St=t.indexOf("--clock-skew");St!==-1&&St<t.length-1&&(e.clockSkew=Number.parseInt(t[St+1],10)),e.clockSkew===void 0&&process.env.SAP_CLOCK_SKEW&&(e.clockSkew=Number.parseInt(process.env.SAP_CLOCK_SKEW,10));let vt=t.indexOf("--assertion-lifetime");return vt!==-1&&vt<t.length-1&&(e.assertionLifetime=Number.parseInt(t[vt+1],10)),e.assertionLifetime===void 0&&process.env.SAP_ASSERTION_LIFETIME&&(e.assertionLifetime=Number.parseInt(process.env.SAP_ASSERTION_LIFETIME,10)),e}function qt(t){return !!(t.sndprn&&t.rcvprn)}function W(t,e){return !e||e.length===0?true:e.some(n=>minimatch(t,n))}var Wt=z.object({tlsVerify:z.boolean().default(true),clientCert:z.string().optional(),clientKey:z.string().optional(),clientKeyPassphrase:z.string().optional(),requestTimeoutMs:z.number().int().positive().default(3e4),caCertPath:z.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 Tt(t=process.env){let e=t.SAP_TLS_VERIFY!=="false",n=t.SAP_CA_CERT?.trim();n&&!t.NODE_EXTRA_CA_CERTS&&(process.env.NODE_EXTRA_CA_CERTS=n);let r=he(void 0,"SAP_CLIENT_CERT","SAP_CLIENT_CERT_PATH",t),s=he(void 0,"SAP_CLIENT_KEY","SAP_CLIENT_KEY_PATH",t),i=t.SAP_CLIENT_KEY_PASSPHRASE,o=t.SAP_HTTP_TIMEOUT_MS!==void 0?Number.parseInt(t.SAP_HTTP_TIMEOUT_MS,10):void 0,a={tlsVerify:e,...n?{caCertPath:n}:{},...r!==void 0?{clientCert:r}:{},...s!==void 0?{clientKey:s}:{},...i!==void 0?{clientKeyPassphrase:i}:{},...o!==void 0?{requestTimeoutMs:o}:{}},c=Wt.safeParse(a);if(!c.success){let l=c.error.issues.map(u=>` - ${u.path.join(".")||"root"}: ${u.message}`).join(`
2
+ import {readFileSync,existsSync,watchFile,watch,unwatchFile,mkdirSync,writeFileSync,renameSync,unlinkSync}from'fs';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import ps from'pino';import {minimatch}from'minimatch';import {z}from'zod/v4';import {lookup}from'dns/promises';import {connect}from'tls';import Ct,{isAxiosError}from'axios';import {AsyncLocalStorage}from'async_hooks';import {randomUUID,timingSafeEqual,randomBytes,createHash}from'crypto';import {jwtVerify,decodeJwt,createRemoteJWKSet}from'jose';import {SignedXml}from'xml-crypto';import {Agent}from'https';import {resolve,join,dirname}from'path';import {z as z$1}from'zod';import {readFile}from'fs/promises';import {createServer}from'http';import {XMLParser,XMLBuilder}from'fast-xml-parser';import {McpServer,ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {createMcpExpressApp}from'@modelcontextprotocol/sdk/server/express.js';import {SSEServerTransport}from'@modelcontextprotocol/sdk/server/sse.js';import {StreamableHTTPServerTransport}from'@modelcontextprotocol/sdk/server/streamableHttp.js';function he(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 Vt(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 xr(t,e)}function xr(t,e){let n=t.indexOf("--webhook-port");n!==-1&&n<t.length-1&&(e.webhookPort=Number.parseInt(t[n+1],10));let r=t.indexOf("--webhook-secret");r!==-1&&r<t.length-1&&(e.webhookSecret=t[r+1]),!e.webhookSecret&&process.env.SAP_WEBHOOK_SECRET&&(e.webhookSecret=process.env.SAP_WEBHOOK_SECRET),e.webhookSecret!==void 0&&e.webhookSecret.trim().length===0&&(e.webhookSecret=void 0);let s=t.indexOf("--webhook-host");if(s!==-1&&s<t.length-1&&(e.webhookHost=t[s+1]),!e.webhookHost&&process.env.SAP_WEBHOOK_HOST&&(e.webhookHost=process.env.SAP_WEBHOOK_HOST),process.env.SAP_WEBHOOK_MAX_BODY_BYTES){let C=Number.parseInt(process.env.SAP_WEBHOOK_MAX_BODY_BYTES,10);Number.isFinite(C)&&C>0&&(e.webhookMaxBodyBytes=C);}let i=t.indexOf("--transport");if(i!==-1&&i<t.length-1){let C=t[i+1];(C==="stdio"||C==="http"||C==="sse")&&(e.transport=C);}let o=t.indexOf("--port");o!==-1&&o<t.length-1&&(e.port=Number.parseInt(t[o+1],10));let a=t.indexOf("--api-key");a!==-1&&a<t.length-1&&(e.apiKey=t[a+1]);let c=["sndpor","sndprt","sndprn","rcvpor","rcvprt","rcvprn"];for(let C of c){let q=t.indexOf(`--${C}`);q!==-1&&q<t.length-1&&(e[C]=t[q+1]);}let l=t.indexOf("--catalog-v2-url");l!==-1&&l<t.length-1&&(e.catalogV2Url=t[l+1]),!e.catalogV2Url&&process.env.ODATA_V2_CATALOG_PATH&&(e.catalogV2Url=process.env.ODATA_V2_CATALOG_PATH);let u=t.indexOf("--catalog-v4-url");u!==-1&&u<t.length-1&&(e.catalogV4Url=t[u+1]);let d=t.includes("--allow-write")||process.env.SAP_MCP_ALLOW_WRITE==="true";e.readOnly=!d,e.allowWriteExplicit=d;let m=t.indexOf("--tool-timeout"),p=m!==-1&&m<t.length-1?t[m+1]:process.env.SAP_MCP_TOOL_TIMEOUT;if(p!==void 0){let C=Number.parseInt(p,10);if(!Number.isFinite(C)||C<0)throw new Error(`--tool-timeout muss eine Zahl in Sekunden sein (0 schaltet das Budget ab), war: "${p}"`);e.toolTimeoutSeconds=C;}let f=t.indexOf("--cache-dir"),g=f!==-1&&f<t.length-1?t[f+1]:process.env.SAP_CACHE_DIR;g&&g.trim().length>0&&(e.cacheDir=g.trim());let y=t.indexOf("--concurrency"),h=y!==-1&&y<t.length-1?t[y+1]:process.env.SAP_CONCURRENCY;if(h!==void 0){let C=Number.parseInt(h,10);if(!Number.isFinite(C)||C<1)throw new Error(`--concurrency muss eine Zahl >= 1 sein, war: "${h}"`);e.concurrency=C;}let S=t.indexOf("--destinations"),v=S!==-1&&S<t.length-1?t[S+1]:process.env.SAP_MCP_DESTINATIONS;v&&v.trim().length>0&&(e.destinationsFile=v.trim());let k=t.indexOf("--help"),L=t.indexOf("-h");(k!==-1||L!==-1)&&(e.help=true);let F=t.indexOf("--tiers");if(F!==-1&&F<t.length-1){let C=t[F+1].split(",").map(q=>q.trim()).filter(q=>q.length>0);e.tiers=C.length>0?C:void 0;}let Q=t.indexOf("--destination-name");Q!==-1&&Q<t.length-1&&(e.destinationName=t[Q+1]),!e.destinationName&&process.env.SAP_DESTINATION_NAME&&(e.destinationName=process.env.SAP_DESTINATION_NAME);let mt=t.indexOf("--destination-service-url");mt!==-1&&mt<t.length-1&&(e.destinationServiceUrl=t[mt+1]),!e.destinationServiceUrl&&process.env.SAP_DESTINATION_SERVICE_URL&&(e.destinationServiceUrl=process.env.SAP_DESTINATION_SERVICE_URL);let ft=t.indexOf("--issuer");ft!==-1&&ft<t.length-1&&(e.issuer=t[ft+1]),!e.issuer&&process.env.SAP_ISSUER&&(e.issuer=process.env.SAP_ISSUER);let gt=t.indexOf("--name-id-format");gt!==-1&&gt<t.length-1&&(e.nameIdFormat=t[gt+1]),!e.nameIdFormat&&process.env.SAP_NAME_ID_FORMAT&&(e.nameIdFormat=process.env.SAP_NAME_ID_FORMAT);let ht=t.indexOf("--private-key"),Ir=ht!==-1&&ht<t.length-1?t[ht+1]:void 0;e.privateKey=he(Ir,"SAP_PRIVATE_KEY","SAP_PRIVATE_KEY_FILE");let yt=t.indexOf("--certificate"),Or=yt!==-1&&yt<t.length-1?t[yt+1]:void 0;e.certificate=he(Or,"SAP_CERTIFICATE","SAP_CERTIFICATE_FILE");let St=t.indexOf("--clock-skew");St!==-1&&St<t.length-1&&(e.clockSkew=Number.parseInt(t[St+1],10)),e.clockSkew===void 0&&process.env.SAP_CLOCK_SKEW&&(e.clockSkew=Number.parseInt(process.env.SAP_CLOCK_SKEW,10));let vt=t.indexOf("--assertion-lifetime");return vt!==-1&&vt<t.length-1&&(e.assertionLifetime=Number.parseInt(t[vt+1],10)),e.assertionLifetime===void 0&&process.env.SAP_ASSERTION_LIFETIME&&(e.assertionLifetime=Number.parseInt(process.env.SAP_ASSERTION_LIFETIME,10)),e}function qt(t){return !!(t.sndprn&&t.rcvprn)}function W(t,e){return !e||e.length===0?true:e.some(n=>minimatch(t,n))}var Wt=z.object({tlsVerify:z.boolean().default(true),clientCert:z.string().optional(),clientKey:z.string().optional(),clientKeyPassphrase:z.string().optional(),requestTimeoutMs:z.number().int().positive().default(3e4),caCertPath:z.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 bt(t=process.env){let e=t.SAP_TLS_VERIFY!=="false",n=t.SAP_CA_CERT?.trim();n&&!t.NODE_EXTRA_CA_CERTS&&(process.env.NODE_EXTRA_CA_CERTS=n);let r=he(void 0,"SAP_CLIENT_CERT","SAP_CLIENT_CERT_PATH",t),s=he(void 0,"SAP_CLIENT_KEY","SAP_CLIENT_KEY_PATH",t),i=t.SAP_CLIENT_KEY_PASSPHRASE,o=t.SAP_HTTP_TIMEOUT_MS!==void 0?Number.parseInt(t.SAP_HTTP_TIMEOUT_MS,10):void 0,a={tlsVerify:e,...n?{caCertPath:n}:{},...r!==void 0?{clientCert:r}:{},...s!==void 0?{clientKey:s}:{},...i!==void 0?{clientKeyPassphrase:i}:{},...o!==void 0?{requestTimeoutMs:o}:{}},c=Wt.safeParse(a);if(!c.success){let l=c.error.issues.map(u=>` - ${u.path.join(".")||"root"}: ${u.message}`).join(`
3
3
  `);throw new Error(`Invalid HTTP options:
4
- ${l}`)}return c.data}function Me(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 Q=new AsyncLocalStorage;function zt(){return randomBytes(6).toString("hex")}function Z(){return Q.getStore()}function Kt(t,e){return Q.run(t,e)}function Ue(){let t=Q.getStore();if(t)return t.sapCalls.count+=1,t.correlationId}function Gt(t=Date.now()){let e=Q.getStore();if(e?.deadline)return Math.max(0,e.deadline-t)}function $e(){return Q.getStore()?.signal?.aborted??false}var D=class extends Error{timedOut;constructor(e,n){super(e?`Zeitbudget f\xFCr ${n??"den Tool-Aufruf"} \xFCberschritten \u2014 abgebrochen.`:`Client hat die Verbindung verworfen \u2014 ${n??"der Tool-Aufruf"} wurde abgebrochen.`),this.name="ToolCallAbortedError",this.timedOut=e;}};function Le(){let t=Q.getStore();if(!t?.signal?.aborted)return;let e=t.signal.reason?.name==="TimeoutError";throw new D(e,t.toolName)}function Yt(t,e){if(!e||e<=0)return {signal:t,deadline:void 0};let n=AbortSignal.timeout(e),r=Date.now()+e;return t?{signal:AbortSignal.any([t,n]),deadline:r}:{signal:n,deadline:r}}function 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 bt(t){if(typeof t!="string")return false;let e=t.trimStart().slice(0,200).toLowerCase();return e.startsWith("<!doctype")||e.startsWith("<html")||e.includes("<title")}function wt(t){let e=/<title[^>]*>([^<]{1,200})<\/title>/i.exec(t)?.[1]?.trim();return e||t.replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<[^>]+>/g," ").replace(/\s+/g," ").trim().slice(0,200)||"HTML-Seite ohne verwertbaren Text"}function $r(t){return /\/IW[A-Z]+\/[A-Z_]+\/\d+/i.exec(t)?.[0]}var Lr=new Set(["UNABLE_TO_GET_ISSUER_CERT_LOCALLY","UNABLE_TO_VERIFY_LEAF_SIGNATURE","SELF_SIGNED_CERT_IN_CHAIN","DEPTH_ZERO_SELF_SIGNED_CERT","CERT_UNTRUSTED"]);function Fr(t){let e=t?.code;if(typeof e=="string")return e;let n=t?.cause;if(typeof n?.code=="string")return n.code}function jr(t){let n=(t?.cause?.cert??t?.cert)?.issuer?.CN;return typeof n=="string"?n:void 0}function te(t,e){if(t instanceof D)return {code:t.timedOut?"TOOL_TIMEOUT":"ABORTED",message:t.message};let n=Fr(t);if(n&&Lr.has(n)){let i=jr(t);return {code:"TLS_CHAIN_INCOMPLETE",message:i?`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar (${n}). Aussteller: ${i}.`:`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar (${n}).`,guidance:"Root-CA \xFCber SAP_CA_CERT=<pfad> einbinden (intern NODE_EXTRA_CA_CERTS). Welches Zertifikat fehlt, zeigt `test-connection` (Stufe TLS) oder `openssl s_client -connect <host>:<port> -showcerts`. SAP_TLS_VERIFY=false ist kein Ersatz \u2014 es schaltet die Pr\xFCfung nur ab."}}if(n==="ENOTFOUND"||n==="EAI_AGAIN")return {code:"DNS_FAILED",message:`Der SAP-Host ist nicht aufl\xF6sbar (${n}).`,guidance:"SAP_BASE_URL pr\xFCfen. Im Container zus\xE4tzlich die DNS-Konfiguration."};if(n==="ECONNREFUSED")return {code:"CONNECTION_REFUSED",message:"Der SAP-Host nimmt keine Verbindung auf dem angegebenen Port an.",guidance:"Port in SAP_BASE_URL pr\xFCfen (HTTPS-Port des ICM, oft 44300 oder 8443)."};if(n==="ECONNABORTED"||n==="ETIMEDOUT")return {code:"TIMEOUT",message:"Das SAP-System hat nicht rechtzeitig geantwortet.",guidance:"Anfrage eingrenzen (top/skip, filter) oder SAP_HTTP_TIMEOUT_MS erh\xF6hen \u2014 aber unterhalb der kleinsten Client-Grenze bleiben."};if(isAxiosError(t)&&t.response)return Hr(t.response.status,t.response.data,e);let r=t;if(typeof r?.message=="string"&&r.message.length>0)return bt(r.message)?{code:"HTML_RESPONSE",message:`SAP hat eine HTML-Seite statt Daten geliefert: ${wt(r.message)}`,guidance:"Meist eine abgelehnte Anmeldung oder ein ung\xFCltiger Pfad. Ist sap-client gesetzt? Ist der Service in /IWFND/MAINT_SERVICE aktiviert?"}:{code:typeof r.code=="string"?r.code:"ERROR",message:r.message};if(t instanceof Error)return bt(t.message)?{code:"HTML_RESPONSE",message:`SAP hat eine HTML-Seite statt Daten geliefert: ${wt(t.message)}`,guidance:"Meist eine abgelehnte Anmeldung oder ein ung\xFCltiger Pfad. Ist sap-client gesetzt? Ist der Service in /IWFND/MAINT_SERVICE aktiviert?"}:{code:"ERROR",message:t.message};if(typeof t=="string")return {code:"ERROR",message:t};let s=t===void 0?void 0:JSON.stringify(t);return {code:"ERROR",message:s?`Unerwarteter Fehler ohne Meldung: ${s}`:"Der Aufruf schlug fehl, ohne einen Fehler zu hinterlassen. Das Serverlog zeigt unter derselben Korrelations-ID, welche SAP-Aufrufe beteiligt waren."}}function Hr(t,e,n){let r=typeof e=="string"?e:JSON.stringify(e??""),s=$r(r);if(s?.toUpperCase().includes("/IWFND/MED/170")){let o=n?.serviceUrl?`Service ${n.serviceUrl}`:"Der Service";return {code:"SERVICE_NOT_REGISTERED",httpStatus:t,sapCode:s,message:`${o} ist im Gateway nicht registriert.`,guidance:"Aktivierung in Transaktion /IWFND/MAINT_SERVICE n\xF6tig. Das ist kein Berechtigungsproblem \u2014 PFCG und SU53 f\xFChren hier nicht weiter."}}if(t===401)return {code:"AUTH_FAILED",httpStatus:401,...s?{sapCode:s}:{},message:"Anmeldung abgelehnt.",guidance:'Ist sap-client gesetzt? Ohne Mandant antwortet das Gateway mit 401 \u201EAnmeldung fehlgeschlagen" statt mit einer Mandantenmeldung. Sonst Benutzer und Passwort pr\xFCfen.'};if(t===403)return {code:"FORBIDDEN",httpStatus:403,...s?{sapCode:s}:{},message:Br(e)??"Zugriff verweigert.",guidance:"Berechtigungen in PFCG pr\xFCfen, Trace \xFCber SU53. Bei OData-Services zus\xE4tzlich das Objekt S_SERVICE."};if(t===404)return {code:"NOT_FOUND",httpStatus:404,...s?{sapCode:s}:{},message:n?.serviceUrl?`Unter ${n.serviceUrl} liegt nichts.`:"Der angeforderte Pfad existiert auf diesem System nicht.",guidance:"Pfad \xFCber sap_discover_services pr\xFCfen. Ein Service kann existieren, aber nicht aktiviert sein (/IWFND/MAINT_SERVICE)."};if(bt(e))return {code:"HTML_RESPONSE",httpStatus:t,message:`SAP hat eine HTML-Fehlerseite geliefert (HTTP ${t}): ${wt(e)}`,guidance:"Der Pfad wird von SAP nicht als OData-Service beantwortet. Bei Katalogpfaden sap_discover_services statt sap_query verwenden."};let i=ee(e);return {code:i.code&&i.code!=="UNKNOWN"?i.code:`HTTP_${t}`,httpStatus:t,...s?{sapCode:s}:{},message:i.message&&i.message!=="Unknown error"&&i.message!=="Unknown SAP error"?i.message:`SAP antwortete mit HTTP ${t} ohne verwertbare Fehlermeldung.`,...i.details.length>0?{guidance:i.details.map(o=>`${o.code}: ${o.message}`).join(" | ")}:{}}}function Br(t){let e=ee(t);if(e.message&&!(e.message==="Unknown error"||e.message==="Unknown SAP error"))return e.message}var Wr=5e3;function Jt(t){let e=t?.issuer?.CN??t?.issuer?.O;return Array.isArray(e)?e.join(", ")||void 0:typeof e=="string"?e:void 0}function _(t,e,n,r,s){return {stage:t,status:n,detail:r,durationMs:Date.now()-e,...s?.guidance?{guidance:s.guidance}:{},...s?.info?{info:s.info}:{}}}function zr(t){try{let e=new URL(t),n=e.protocol==="https:";return {host:e.hostname,port:e.port?Number(e.port):n?443:80,isTls:n}}catch{return}}async function Kr(t,e){let n=Date.now();try{let{address:r,family:s}=await e(t);return _("dns",n,"ok",`${t} l\xF6st auf ${r} auf.`,{info:{address:r,family:s}})}catch(r){let s=te(r);return _("dns",n,"failed",`${t} ist nicht aufl\xF6sbar.`,{guidance:s.guidance??"SAP_BASE_URL pr\xFCfen. Im Container zus\xE4tzlich die DNS-Konfiguration des Docker-Netzes."})}}async function Gr(t,e,n,r,s){let i=Date.now(),o=a=>new Promise((c,l)=>{let u=s({host:t,port:e,rejectUnauthorized:a,servername:t},()=>{let d=u.getPeerCertificate(true);u.destroy(),c(d);});u.setTimeout(r,()=>{u.destroy(),l(Object.assign(new Error(`TLS-Handshake nach ${r} ms ohne Antwort`),{code:"ETIMEDOUT"}));}),u.on("error",d=>{u.destroy(),l(d);});});try{let a=await o(n),c=Jt(a);return _("tls",i,"ok",n?`TLS-Verbindung steht, Zertifikatskette ist verifizierbar.${c?` Aussteller: ${c}.`:""}`:`TLS-Verbindung steht. Die Zertifikatspr\xFCfung ist \xFCber SAP_TLS_VERIFY=false abgeschaltet \u2014 die Kette wurde NICHT verifiziert.${c?` Aussteller: ${c}.`:""}`,{info:{verified:n,...c?{issuer:c}:{},...a?.subject?.CN?{subject:String(a.subject.CN)}:{},...a?.valid_to?{validTo:a.valid_to}:{}}})}catch(a){let c=te(a);if(c.code==="TLS_CHAIN_INCOMPLETE"){let l;try{let u=await o(!1);l=Jt(u);}catch{}return _("tls",i,"failed",l?`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar. Aussteller des Serverzertifikats: ${l}.`:"Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar.",{guidance:c.guidance,...l?{info:{issuer:l}}:{}})}return _("tls",i,"failed",c.message,{guidance:c.guidance})}}async function Yr(t,e){let n=Date.now();try{let r=await t.request({method:"get",url:"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/",headers:{Accept:"application/json"},params:{$format:"json"}});return [_("auth",n,"ok","Anmeldung am Gateway erfolgreich."),_("client",n,"ok",e?`Mandant ${e} wird an jede Anfrage geh\xE4ngt und vom Gateway angenommen.`:"Kein SAP_CLIENT konfiguriert \u2014 das System hat die Anfrage dennoch angenommen. Auf mandantenabh\xE4ngigen Systemen sollte er gesetzt sein.",{info:{sapClient:e??null}}),_("catalog",n,"ok",`Servicekatalog erreichbar (HTTP ${r.status}).`)]}catch(r){let s=te(r);return s.httpStatus===401&&!e?[_("auth",n,"ok","Zugangsdaten wurden gesendet."),_("client",n,"failed",'Das Gateway lehnt die Anmeldung ab, und SAP_CLIENT ist nicht gesetzt. Gemessen antwortet das Gateway ohne sap-client mit 401 \u201EAnmeldung fehlgeschlagen" statt mit einer Mandantenmeldung.',{guidance:"SAP_CLIENT setzen (z. B. 100) und erneut pr\xFCfen. Bleibt der 401 bestehen, liegt es an Benutzer oder Passwort."}),_("catalog",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor.")]:s.httpStatus===401?[_("auth",n,"failed","Anmeldung abgelehnt.",{guidance:`Benutzer und Passwort pr\xFCfen. Mandant ${e} wird gesendet \u2014 bei Zweifeln in SU01 pr\xFCfen, ob der Benutzer in diesem Mandanten existiert und nicht gesperrt ist.`}),_("client",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor."),_("catalog",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor.")]:[_("auth",n,"ok","Anmeldung am Gateway erfolgreich."),_("client",n,"ok",e?`Mandant ${e} angenommen.`:"Kein Mandant konfiguriert."),_("catalog",n,"failed",s.message,{guidance:s.guidance??"Katalogdienst in /IWFND/MAINT_SERVICE pr\xFCfen. Der Server probiert drei bekannte Katalogpfade durch."})]}}async function je(t,e){let n=Date.now(),r=e.budgetMs??Wr,s=e.deps?.lookup??lookup,i=e.deps?.connectTls??connect,o=[],a=zr(e.baseUrl);if(!a)return o.push(_("dns",n,"failed",`SAP_BASE_URL ist keine g\xFCltige URL: "${e.baseUrl}"`,{guidance:"Erwartet wird etwa https://host.example.com:44300 \u2014 mit Schema und ohne Pfad."}),...["tls","auth","client","catalog"].map(c=>_(c,n,"skipped","Nicht gepr\xFCft \u2014 die Basis-URL ist unbrauchbar."))),Fe(o,n);if(o.push(await Kr(a.host,s)),o[0].status==="failed")return o.push(...["tls","auth","client","catalog"].map(c=>_(c,n,"skipped","Nicht gepr\xFCft \u2014 der Name war nicht aufl\xF6sbar."))),Fe(o,n);if(a.isTls){let c=Math.max(500,r-(Date.now()-n));if(o.push(await Gr(a.host,a.port,e.tlsVerify!==false,c,i)),o[o.length-1].status==="failed")return o.push(...["auth","client","catalog"].map(l=>_(l,n,"skipped","Nicht gepr\xFCft \u2014 die TLS-Stufe scheiterte zuvor."))),Fe(o,n)}else o.push(_("tls",n,"skipped","Nicht gepr\xFCft \u2014 SAP_BASE_URL verwendet http, nicht https."));return o.push(...await Yr(t,e.sapClient)),Fe(o,n)}function Fe(t,e){let n=t.find(r=>r.status==="failed");return {status:n?"failed":"connected",...n?{failedStage:n.stage}:{},stages:t,durationMs:Date.now()-e,timestamp:new Date().toISOString()}}var ne=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 Ct.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 At(t){return createHash("sha256").update(t).digest("hex")}var ye=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});}},He=class{clientTokenManager;cache;config;constructor(e){this.config=e,this.clientTokenManager=new ne(e.tokenServiceUrl,e.clientId,e.clientSecret),this.cache=new ye;}async getToken(e){if(!e)return this.clientTokenManager.getToken();let n=At(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 Ct.get(s,{headers:{Authorization:`Bearer ${r}`,"X-user-token":e}})).data.authTokens;if(!o?.length)throw new Error(`Destination '${this.config.destinationName}': no authTokens in response`);let a=o[0];if(a.error)throw new Error(`Destination '${this.config.destinationName}' token exchange failed: ${a.error}`);let c=parseInt(a.expires_in,10),l=Number.isFinite(c)&&c>0?c:3600,u=Date.now()+(l-60)*1e3;return this.cache.set(n,a.value,u),a.value}};var Se=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 Ct.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 K=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 Ct.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 as={email:"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",sapUsername:"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"};function Xt(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 cs(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 ls(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 Be=class{cache;config;constructor(e){Xt(e.privateKey,"private key"),Xt(e.certificate,"certificate"),this.config={...e,clockSkew:e.clockSkew??120,assertionLifetime:e.assertionLifetime??300},this.cache=new ye;}async getToken(e){if(!e)throw new Error("saml-bearer auth requires a user token for principal propagation");let n=At(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=as[this.config.nameIdFormat],a=new Date(s-this.config.clockSkew*1e3).toISOString(),c=new Date(s+this.config.assertionLifetime*1e3).toISOString(),l=new Date(s).toISOString(),u=cs({assertionId:`_${randomUUID()}`,issuer:this.config.issuer,nameId:i,nameIdFormat:o,recipient:this.config.tokenServiceUrl,audience:this.config.clientId,notBefore:a,notOnOrAfter:c,authnInstant:l}),d=ls(u,this.config.privateKey,this.config.certificate),m=Buffer.from(d).toString("base64"),p=await Ct.post(this.config.tokenServiceUrl,new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:saml2-bearer",assertion:m,client_id:this.config.clientId}).toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}}),{access_token:f,expires_in:g}=p.data,y=typeof g=="number"&&g>0?g:3600,h=Date.now()+(y-60)*1e3;return this.cache.set(n,f,h),f}};function Qt(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 ps=3e4,j=us({name:"sap-http-client",transport:{target:"pino/file",options:{destination:2}}});function ms(t){return t.startsWith("http://")||t.startsWith("https://")}function fs(t){let e=t.replace(/\/+$/,"");try{let n=new URL(e),r=n.pathname.replace(/\/+$/,"");return {origin:`${n.protocol}//${n.host}`,path:r}}catch{return {origin:e,path:""}}}var re=class{constructor(e,n={}){this.destination=e;switch(this.httpsAgent=Qt(n),this.requestTimeoutMs=n.requestTimeoutMs??ps,e.auth.type){case "oauth2":this.tokenManager=new ne(e.auth.tokenServiceUrl,e.auth.clientId,e.auth.clientSecret);break;case "ias":this.tokenManager=new Se({issuerUrl:e.auth.tenantUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new K);break;case "xsuaa":this.tokenManager=new Se({issuerUrl:e.auth.xsuaaUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new K);break;case "btp-principal":this.tokenManager=new He({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 Be({tokenServiceUrl:e.auth.tokenServiceUrl,clientId:e.auth.clientId,issuer:e.auth.issuer,nameIdFormat:e.auth.nameIdFormat,privateKey:e.auth.privateKey,certificate:e.auth.certificate,clockSkew:e.auth.clockSkew,assertionLifetime:e.auth.assertionLifetime});break;default:this.tokenManager=null;}}tokenManager;userContext=null;httpsAgent;requestTimeoutMs;async request(e){Le();let n=e.method!=="get",r=Z(),s=Ue(),i={...e.params};this.destination.sapClient&&(i["sap-client"]=this.destination.sapClient);let o=e.userContext??this.userContext??void 0,a=await this.buildAuthHeaders(o),c=this.destination.headers??{},l={};if(n){let p=await this.fetchCsrfToken({...c,...a},e.url);p.token&&(l={"x-csrf-token":p.token},p.cookies&&(l.Cookie=p.cookies));}let u={...c,...a,...l,...e.headers},d={method:e.method,url:this.resolveUrl(e.url),data:e.data,headers:u,params:Object.keys(i).length>0?i:void 0,httpsAgent:this.httpsAgent,timeout:this.effectiveTimeoutMs(),signal:r?.signal,beforeRedirect:p=>{this.httpsAgent&&(p.agent=this.httpsAgent);let f=p.headers??{};for(let[g,y]of Object.entries(u))f[g]||(f[g]=y);}},m={...d.headers};m.Authorization&&(m.Authorization=`${m.Authorization.substring(0,15)}***`),j.info({...s?{correlationId:s,tool:r?.toolName}:{},method:d.method,url:d.url,headers:m,params:d.params,hasAuth:!!d.headers.Authorization||!!d.headers.APIKey},"SAP HTTP request outgoing");try{let p=await Ct(d);if(j.info({...s?{correlationId:s}:{},status:p.status,contentType:p.headers["content-type"]},"SAP HTTP response OK"),typeof p.data=="string"&&p.data.trimStart().startsWith("<!DOCTYPE"))throw new Error("SAP returned an HTML page instead of JSON. This usually means authentication failed or the URL path is invalid.");return {data:p.data,headers:p.headers,status:p.status}}catch(p){if(isAxiosError(p)&&p.response){let f=e.expectedStatuses?.includes(p.response.status)??false;(f?j.info.bind(j):j.error.bind(j))({...s?{correlationId:s}:{},status:p.response.status,statusText:p.response.statusText,contentType:p.response.headers?.["content-type"],url:d.url,...f?{expected:true}:{},dataPreview:typeof p.response.data=="string"?p.response.data.substring(0,200):typeof p.response.data},f?"SAP HTTP response (expected)":"SAP HTTP response ERROR");}else p instanceof Error&&j.error({message:p.message,name:p.name},"SAP HTTP connection error");if(n&&this.isCsrf403(p)){Le();try{let f=await this.fetchCsrfToken({...c,...a},e.url),g={...d.headers};f.token&&(g["x-csrf-token"]=f.token,f.cookies&&(g.Cookie=f.cookies)),Ue();let y=await Ct({...d,headers:g});return {data:y.data,headers:y.headers,status:y.status}}catch(f){throw this.toSapError(f)}}throw this.toSapError(p)}}async buildAuthHeaders(e){switch(this.destination.auth.type){case "basic":return {Authorization:`Basic ${Buffer.from(`${this.destination.auth.username}:${this.destination.auth.password}`).toString("base64")}`};case "saml-bearer":case "btp-principal":case "oauth2":case "ias":case "xsuaa":return {Authorization:`Bearer ${await this.tokenManager.getToken(e?.token)}`};case "apikey":return {APIKey:this.destination.auth.apiKey};default:return {}}}resolveUrl(e){if(ms(e))return e;let{origin:n,path:r}=fs(this.destination.url);return r&&(e===r||e.startsWith(`${r}/`))?`${n}${e}`:`${this.destination.url}${e}`}async fetchCsrfToken(e,n){let r=this.destination.url,s=[];if(n){let i=n.replace(/\/[^/]*$/,"/");i!=="/"&&i!==n&&s.push(this.resolveUrl(i));}s.push(`${r}/`,r);for(let i of s){Le(),Ue();try{let o={...e,"x-csrf-token":"fetch"},a={};this.destination.sapClient&&(a["sap-client"]=this.destination.sapClient);let c=await Ct.get(i,{headers:o,params:Object.keys(a).length>0?a:void 0,validateStatus:()=>!0,timeout:this.effectiveTimeoutMs(),signal:Z()?.signal,httpsAgent:this.httpsAgent,beforeRedirect:u=>{this.httpsAgent&&(u.agent=this.httpsAgent);let d=u.headers??{};for(let[m,p]of Object.entries(o))d[m]||(d[m]=p);}}),l=c.headers["x-csrf-token"];if(j.debug({url:i,status:c.status,hasToken:!!l,tokenValue:l?.substring(0,8)},"CSRF token fetch result"),l&&l.toLowerCase()!=="required"){let d=(c.headers["set-cookie"]??[]).map(m=>m.split(";")[0]).join("; ");return {token:l,cookies:d}}}catch{}}return {token:"",cookies:""}}isCsrf403(e){return isAxiosError(e)&&e.response?.status===403}effectiveTimeoutMs(){let e=Gt();return e===void 0?this.requestTimeoutMs:Math.max(1,Math.min(this.requestTimeoutMs,e))}toSapError(e){if(e instanceof D)return {code:e.timedOut?"TOOL_TIMEOUT":"ABORTED",message:e.message,details:[],rawResponse:void 0};if(isAxiosError(e)&&(e.code==="ERR_CANCELED"||e.name==="CanceledError")){let n=Z()?.signal?.reason?.name==="TimeoutError";return {code:n?"TOOL_TIMEOUT":"ABORTED",message:n?"Zeitbudget des Tool-Aufrufs \xFCberschritten \u2014 laufender SAP-Aufruf abgebrochen.":"Client hat die Verbindung verworfen \u2014 laufender SAP-Aufruf abgebrochen.",details:[],rawResponse:void 0}}return isAxiosError(e)&&(e.code==="ECONNABORTED"||e.code==="ETIMEDOUT")?{code:"TIMEOUT",message:`SAP request exceeded ${this.requestTimeoutMs} ms (SAP_HTTP_TIMEOUT_MS): ${e.message}`,details:[],rawResponse:void 0}:isAxiosError(e)&&e.response?ee(e.response.data):e instanceof Error?{code:"CONNECTION_ERROR",message:e.message,details:[],rawResponse:void 0}:ee(e)}};function Zt(t,e){e||t.warn({sapClient:null},'SAP_CLIENT ist nicht gesetzt. Auf mandantenabh\xE4ngigen Systemen antwortet das Gateway ohne sap-client mit 401 \u201EAnmeldung fehlgeschlagen" \u2014 was wie ein falsches Passwort aussieht, aber keines ist. Bei Anmeldeproblemen zuerst SAP_CLIENT setzen (z. B. 100).');}async function en(t,e,n,r){try{let s=new re(Me(e),n),i=await je(s,{baseUrl:e.baseUrl,sapClient:e.sapClient,tlsVerify:n.tlsVerify,...r?{deps:r}:{}});for(let o of i.stages){let a={stage:o.stage,status:o.status,durationMs:o.durationMs,...o.info??{},...o.guidance?{guidance:o.guidance}:{}};o.status==="failed"?t.warn(a,`Selbsttest ${o.stage}: ${o.detail}`):t.info(a,`Selbsttest ${o.stage}: ${o.detail}`);}i.status==="connected"?t.info({durationMs:i.durationMs,sapClient:e.sapClient??null},"Selbsttest bestanden: Namensaufl\xF6sung, TLS, Anmeldung, Mandant und Katalog sind in Ordnung"):t.warn({failedStage:i.failedStage,durationMs:i.durationMs},`Selbsttest gescheitert in Stufe \u201E${i.failedStage}". Der Server l\xE4uft weiter \u2014 Tool-Aufrufe werden bis zur Behebung fehlschlagen. Einzelheiten liefert das Tool test-connection.`);}catch(s){t.warn({err:s instanceof Error?s.message:String(s)},"Selbsttest konnte nicht ausgef\xFChrt werden. Der Server l\xE4uft weiter.");}}var G=z$1.object({name:z$1.string().default("default"),baseUrl:z$1.url(),sapClient:z$1.string().optional()}),gs=G.extend({authType:z$1.literal("basic"),username:z$1.string().min(1),password:z$1.string().min(1)}),hs=G.extend({authType:z$1.literal("oauth2"),clientId:z$1.string().min(1),clientSecret:z$1.string().min(1),tokenServiceUrl:z$1.url()}),ys=G.extend({authType:z$1.literal("apikey"),apiKey:z$1.string().min(1)}),Ss=G.extend({authType:z$1.literal("ias"),tenantUrl:z$1.url(),clientId:z$1.string().min(1),clientSecret:z$1.string().min(1)}),vs=G.extend({authType:z$1.literal("xsuaa"),xsuaaUrl:z$1.url(),clientId:z$1.string().min(1),clientSecret:z$1.string().min(1)}),Ts=G.extend({authType:z$1.literal("btp-principal"),destinationName:z$1.string().min(1),destinationServiceUrl:z$1.url(),clientId:z$1.string().min(1),clientSecret:z$1.string().min(1),tokenServiceUrl:z$1.url()}),bs=G.extend({authType:z$1.literal("saml-bearer"),tokenServiceUrl:z$1.url(),clientId:z$1.string().min(1),issuer:z$1.string().min(1),nameIdFormat:z$1.enum(["email","sapUsername"]).default("email"),privateKey:z$1.string().min(1),certificate:z$1.string().min(1),clockSkew:z$1.coerce.number().int().nonnegative().default(120),assertionLifetime:z$1.coerce.number().int().positive().default(300)}),se=z$1.discriminatedUnion("authType",[gs,hs,ys,Ss,vs,Ts,bs]);function Te(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=se.safeParse(n);if(!r.success){let s=r.error.issues.map(i=>` - ${i.path.join(".")||"root"}: ${i.message}`).join(`
4
+ ${l}`)}return c.data}function Me(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 Z=new AsyncLocalStorage;function zt(){return randomBytes(6).toString("hex")}function ee(){return Z.getStore()}function Kt(t,e){return Z.run(t,e)}function Ue(){let t=Z.getStore();if(t)return t.sapCalls.count+=1,t.correlationId}function Gt(t=Date.now()){let e=Z.getStore();if(e?.deadline)return Math.max(0,e.deadline-t)}function $e(){return Z.getStore()?.signal?.aborted??false}var D=class extends Error{timedOut;constructor(e,n){super(e?`Zeitbudget f\xFCr ${n??"den Tool-Aufruf"} \xFCberschritten \u2014 abgebrochen.`:`Client hat die Verbindung verworfen \u2014 ${n??"der Tool-Aufruf"} wurde abgebrochen.`),this.name="ToolCallAbortedError",this.timedOut=e;}};function Le(){let t=Z.getStore();if(!t?.signal?.aborted)return;let e=t.signal.reason?.name==="TimeoutError";throw new D(e,t.toolName)}function Yt(t,e){if(!e||e<=0)return {signal:t,deadline:void 0};let n=AbortSignal.timeout(e),r=Date.now()+e;return t?{signal:AbortSignal.any([t,n]),deadline:r}:{signal:n,deadline:r}}function te(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 Tt(t){if(typeof t!="string")return false;let e=t.trimStart().slice(0,200).toLowerCase();return e.startsWith("<!doctype")||e.startsWith("<html")||e.includes("<title")}function wt(t){let e=/<title[^>]*>([^<]{1,200})<\/title>/i.exec(t)?.[1]?.trim();return e||t.replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<[^>]+>/g," ").replace(/\s+/g," ").trim().slice(0,200)||"HTML-Seite ohne verwertbaren Text"}function Lr(t){return /\/IW[A-Z]+\/[A-Z_]+\/\d+/i.exec(t)?.[0]}var Fr=new Set(["UNABLE_TO_GET_ISSUER_CERT_LOCALLY","UNABLE_TO_VERIFY_LEAF_SIGNATURE","SELF_SIGNED_CERT_IN_CHAIN","DEPTH_ZERO_SELF_SIGNED_CERT","CERT_UNTRUSTED"]);function jr(t){let e=t?.code;if(typeof e=="string")return e;let n=t?.cause;if(typeof n?.code=="string")return n.code}function Hr(t){let n=(t?.cause?.cert??t?.cert)?.issuer?.CN;return typeof n=="string"?n:void 0}function ne(t,e){if(t instanceof D)return {code:t.timedOut?"TOOL_TIMEOUT":"ABORTED",message:t.message};let n=jr(t);if(n&&Fr.has(n)){let i=Hr(t);return {code:"TLS_CHAIN_INCOMPLETE",message:i?`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar (${n}). Aussteller: ${i}.`:`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar (${n}).`,guidance:"Root-CA \xFCber SAP_CA_CERT=<pfad> einbinden (intern NODE_EXTRA_CA_CERTS). Welches Zertifikat fehlt, zeigt `test-connection` (Stufe TLS) oder `openssl s_client -connect <host>:<port> -showcerts`. SAP_TLS_VERIFY=false ist kein Ersatz \u2014 es schaltet die Pr\xFCfung nur ab."}}if(n==="ENOTFOUND"||n==="EAI_AGAIN")return {code:"DNS_FAILED",message:`Der SAP-Host ist nicht aufl\xF6sbar (${n}).`,guidance:"SAP_BASE_URL pr\xFCfen. Im Container zus\xE4tzlich die DNS-Konfiguration."};if(n==="ECONNREFUSED")return {code:"CONNECTION_REFUSED",message:"Der SAP-Host nimmt keine Verbindung auf dem angegebenen Port an.",guidance:"Port in SAP_BASE_URL pr\xFCfen (HTTPS-Port des ICM, oft 44300 oder 8443)."};if(n==="ECONNABORTED"||n==="ETIMEDOUT")return {code:"TIMEOUT",message:"Das SAP-System hat nicht rechtzeitig geantwortet.",guidance:"Anfrage eingrenzen (top/skip, filter) oder SAP_HTTP_TIMEOUT_MS erh\xF6hen \u2014 aber unterhalb der kleinsten Client-Grenze bleiben."};if(isAxiosError(t)&&t.response)return Br(t.response.status,t.response.data,e);let r=t;if(typeof r?.message=="string"&&r.message.length>0)return Tt(r.message)?{code:"HTML_RESPONSE",message:`SAP hat eine HTML-Seite statt Daten geliefert: ${wt(r.message)}`,guidance:"Meist eine abgelehnte Anmeldung oder ein ung\xFCltiger Pfad. Ist sap-client gesetzt? Ist der Service in /IWFND/MAINT_SERVICE aktiviert?"}:{code:typeof r.code=="string"?r.code:"ERROR",message:r.message};if(t instanceof Error)return Tt(t.message)?{code:"HTML_RESPONSE",message:`SAP hat eine HTML-Seite statt Daten geliefert: ${wt(t.message)}`,guidance:"Meist eine abgelehnte Anmeldung oder ein ung\xFCltiger Pfad. Ist sap-client gesetzt? Ist der Service in /IWFND/MAINT_SERVICE aktiviert?"}:{code:"ERROR",message:t.message};if(typeof t=="string")return {code:"ERROR",message:t};let s=t===void 0?void 0:JSON.stringify(t);return {code:"ERROR",message:s?`Unerwarteter Fehler ohne Meldung: ${s}`:"Der Aufruf schlug fehl, ohne einen Fehler zu hinterlassen. Das Serverlog zeigt unter derselben Korrelations-ID, welche SAP-Aufrufe beteiligt waren."}}function Br(t,e,n){let r=typeof e=="string"?e:JSON.stringify(e??""),s=Lr(r);if(s?.toUpperCase().includes("/IWFND/MED/170")){let o=n?.serviceUrl?`Service ${n.serviceUrl}`:"Der Service";return {code:"SERVICE_NOT_REGISTERED",httpStatus:t,sapCode:s,message:`${o} ist im Gateway nicht registriert.`,guidance:"Aktivierung in Transaktion /IWFND/MAINT_SERVICE n\xF6tig. Das ist kein Berechtigungsproblem \u2014 PFCG und SU53 f\xFChren hier nicht weiter."}}if(t===401)return {code:"AUTH_FAILED",httpStatus:401,...s?{sapCode:s}:{},message:"Anmeldung abgelehnt.",guidance:'Ist sap-client gesetzt? Ohne Mandant antwortet das Gateway mit 401 \u201EAnmeldung fehlgeschlagen" statt mit einer Mandantenmeldung. Sonst Benutzer und Passwort pr\xFCfen.'};if(t===403)return {code:"FORBIDDEN",httpStatus:403,...s?{sapCode:s}:{},message:Vr(e)??"Zugriff verweigert.",guidance:"Berechtigungen in PFCG pr\xFCfen, Trace \xFCber SU53. Bei OData-Services zus\xE4tzlich das Objekt S_SERVICE."};if(t===404)return {code:"NOT_FOUND",httpStatus:404,...s?{sapCode:s}:{},message:n?.serviceUrl?`Unter ${n.serviceUrl} liegt nichts.`:"Der angeforderte Pfad existiert auf diesem System nicht.",guidance:"Pfad \xFCber sap_discover_services pr\xFCfen. Ein Service kann existieren, aber nicht aktiviert sein (/IWFND/MAINT_SERVICE)."};if(Tt(e))return {code:"HTML_RESPONSE",httpStatus:t,message:`SAP hat eine HTML-Fehlerseite geliefert (HTTP ${t}): ${wt(e)}`,guidance:"Der Pfad wird von SAP nicht als OData-Service beantwortet. Bei Katalogpfaden sap_discover_services statt sap_query verwenden."};let i=te(e);return {code:i.code&&i.code!=="UNKNOWN"?i.code:`HTTP_${t}`,httpStatus:t,...s?{sapCode:s}:{},message:i.message&&i.message!=="Unknown error"&&i.message!=="Unknown SAP error"?i.message:`SAP antwortete mit HTTP ${t} ohne verwertbare Fehlermeldung.`,...i.details.length>0?{guidance:i.details.map(o=>`${o.code}: ${o.message}`).join(" | ")}:{}}}function Vr(t){let e=te(t);if(e.message&&!(e.message==="Unknown error"||e.message==="Unknown SAP error"))return e.message}var zr=5e3;function Jt(t){let e=t?.issuer?.CN??t?.issuer?.O;return Array.isArray(e)?e.join(", ")||void 0:typeof e=="string"?e:void 0}function _(t,e,n,r,s){return {stage:t,status:n,detail:r,durationMs:Date.now()-e,...s?.guidance?{guidance:s.guidance}:{},...s?.info?{info:s.info}:{}}}function Kr(t){try{let e=new URL(t),n=e.protocol==="https:";return {host:e.hostname,port:e.port?Number(e.port):n?443:80,isTls:n}}catch{return}}async function Gr(t,e){let n=Date.now();try{let{address:r,family:s}=await e(t);return _("dns",n,"ok",`${t} l\xF6st auf ${r} auf.`,{info:{address:r,family:s}})}catch(r){let s=ne(r);return _("dns",n,"failed",`${t} ist nicht aufl\xF6sbar.`,{guidance:s.guidance??"SAP_BASE_URL pr\xFCfen. Im Container zus\xE4tzlich die DNS-Konfiguration des Docker-Netzes."})}}async function Yr(t,e,n,r,s){let i=Date.now(),o=a=>new Promise((c,l)=>{let u=s({host:t,port:e,rejectUnauthorized:a,servername:t},()=>{let d=u.getPeerCertificate(true);u.destroy(),c(d);});u.setTimeout(r,()=>{u.destroy(),l(Object.assign(new Error(`TLS-Handshake nach ${r} ms ohne Antwort`),{code:"ETIMEDOUT"}));}),u.on("error",d=>{u.destroy(),l(d);});});try{let a=await o(n),c=Jt(a);return _("tls",i,"ok",n?`TLS-Verbindung steht, Zertifikatskette ist verifizierbar.${c?` Aussteller: ${c}.`:""}`:`TLS-Verbindung steht. Die Zertifikatspr\xFCfung ist \xFCber SAP_TLS_VERIFY=false abgeschaltet \u2014 die Kette wurde NICHT verifiziert.${c?` Aussteller: ${c}.`:""}`,{info:{verified:n,...c?{issuer:c}:{},...a?.subject?.CN?{subject:String(a.subject.CN)}:{},...a?.valid_to?{validTo:a.valid_to}:{}}})}catch(a){let c=ne(a);if(c.code==="TLS_CHAIN_INCOMPLETE"){let l;try{let u=await o(!1);l=Jt(u);}catch{}return _("tls",i,"failed",l?`Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar. Aussteller des Serverzertifikats: ${l}.`:"Die Zertifikatskette ist nicht vollst\xE4ndig verifizierbar.",{guidance:c.guidance,...l?{info:{issuer:l}}:{}})}return _("tls",i,"failed",c.message,{guidance:c.guidance})}}async function Jr(t,e){let n=Date.now();try{let r=await t.request({method:"get",url:"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/",headers:{Accept:"application/json"},params:{$format:"json"}});return [_("auth",n,"ok","Anmeldung am Gateway erfolgreich."),_("client",n,"ok",e?`Mandant ${e} wird an jede Anfrage geh\xE4ngt und vom Gateway angenommen.`:"Kein SAP_CLIENT konfiguriert \u2014 das System hat die Anfrage dennoch angenommen. Auf mandantenabh\xE4ngigen Systemen sollte er gesetzt sein.",{info:{sapClient:e??null}}),_("catalog",n,"ok",`Servicekatalog erreichbar (HTTP ${r.status}).`)]}catch(r){let s=ne(r);return s.httpStatus===401&&!e?[_("auth",n,"ok","Zugangsdaten wurden gesendet."),_("client",n,"failed",'Das Gateway lehnt die Anmeldung ab, und SAP_CLIENT ist nicht gesetzt. Gemessen antwortet das Gateway ohne sap-client mit 401 \u201EAnmeldung fehlgeschlagen" statt mit einer Mandantenmeldung.',{guidance:"SAP_CLIENT setzen (z. B. 100) und erneut pr\xFCfen. Bleibt der 401 bestehen, liegt es an Benutzer oder Passwort."}),_("catalog",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor.")]:s.httpStatus===401?[_("auth",n,"failed","Anmeldung abgelehnt.",{guidance:`Benutzer und Passwort pr\xFCfen. Mandant ${e} wird gesendet \u2014 bei Zweifeln in SU01 pr\xFCfen, ob der Benutzer in diesem Mandanten existiert und nicht gesperrt ist.`}),_("client",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor."),_("catalog",n,"skipped","Nicht gepr\xFCft \u2014 die Anmeldung scheiterte zuvor.")]:[_("auth",n,"ok","Anmeldung am Gateway erfolgreich."),_("client",n,"ok",e?`Mandant ${e} angenommen.`:"Kein Mandant konfiguriert."),_("catalog",n,"failed",s.message,{guidance:s.guidance??"Katalogdienst in /IWFND/MAINT_SERVICE pr\xFCfen. Der Server probiert drei bekannte Katalogpfade durch."})]}}async function je(t,e){let n=Date.now(),r=e.budgetMs??zr,s=e.deps?.lookup??lookup,i=e.deps?.connectTls??connect,o=[],a=Kr(e.baseUrl);if(!a)return o.push(_("dns",n,"failed",`SAP_BASE_URL ist keine g\xFCltige URL: "${e.baseUrl}"`,{guidance:"Erwartet wird etwa https://host.example.com:44300 \u2014 mit Schema und ohne Pfad."}),...["tls","auth","client","catalog"].map(c=>_(c,n,"skipped","Nicht gepr\xFCft \u2014 die Basis-URL ist unbrauchbar."))),Fe(o,n);if(o.push(await Gr(a.host,s)),o[0].status==="failed")return o.push(...["tls","auth","client","catalog"].map(c=>_(c,n,"skipped","Nicht gepr\xFCft \u2014 der Name war nicht aufl\xF6sbar."))),Fe(o,n);if(a.isTls){let c=Math.max(500,r-(Date.now()-n));if(o.push(await Yr(a.host,a.port,e.tlsVerify!==false,c,i)),o[o.length-1].status==="failed")return o.push(...["auth","client","catalog"].map(l=>_(l,n,"skipped","Nicht gepr\xFCft \u2014 die TLS-Stufe scheiterte zuvor."))),Fe(o,n)}else o.push(_("tls",n,"skipped","Nicht gepr\xFCft \u2014 SAP_BASE_URL verwendet http, nicht https."));return o.push(...await Jr(t,e.sapClient)),Fe(o,n)}function Fe(t,e){let n=t.find(r=>r.status==="failed");return {status:n?"failed":"connected",...n?{failedStage:n.stage}:{},stages:t,durationMs:Date.now()-e,timestamp:new Date().toISOString()}}var re=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 Ct.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 At(t){return createHash("sha256").update(t).digest("hex")}var ye=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});}},He=class{clientTokenManager;cache;config;constructor(e){this.config=e,this.clientTokenManager=new re(e.tokenServiceUrl,e.clientId,e.clientSecret),this.cache=new ye;}async getToken(e){if(!e)return this.clientTokenManager.getToken();let n=At(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 Ct.get(s,{headers:{Authorization:`Bearer ${r}`,"X-user-token":e}})).data.authTokens;if(!o?.length)throw new Error(`Destination '${this.config.destinationName}': no authTokens in response`);let a=o[0];if(a.error)throw new Error(`Destination '${this.config.destinationName}' token exchange failed: ${a.error}`);let c=parseInt(a.expires_in,10),l=Number.isFinite(c)&&c>0?c:3600,u=Date.now()+(l-60)*1e3;return this.cache.set(n,a.value,u),a.value}};var Se=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 Ct.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 K=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 Ct.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 cs={email:"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",sapUsername:"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"};function Xt(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 ls(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 ds(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 Be=class{cache;config;constructor(e){Xt(e.privateKey,"private key"),Xt(e.certificate,"certificate"),this.config={...e,clockSkew:e.clockSkew??120,assertionLifetime:e.assertionLifetime??300},this.cache=new ye;}async getToken(e){if(!e)throw new Error("saml-bearer auth requires a user token for principal propagation");let n=At(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=cs[this.config.nameIdFormat],a=new Date(s-this.config.clockSkew*1e3).toISOString(),c=new Date(s+this.config.assertionLifetime*1e3).toISOString(),l=new Date(s).toISOString(),u=ls({assertionId:`_${randomUUID()}`,issuer:this.config.issuer,nameId:i,nameIdFormat:o,recipient:this.config.tokenServiceUrl,audience:this.config.clientId,notBefore:a,notOnOrAfter:c,authnInstant:l}),d=ds(u,this.config.privateKey,this.config.certificate),m=Buffer.from(d).toString("base64"),p=await Ct.post(this.config.tokenServiceUrl,new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:saml2-bearer",assertion:m,client_id:this.config.clientId}).toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}}),{access_token:f,expires_in:g}=p.data,y=typeof g=="number"&&g>0?g:3600,h=Date.now()+(y-60)*1e3;return this.cache.set(n,f,h),f}};function Qt(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 ms=3e4,j=ps({name:"sap-http-client",transport:{target:"pino/file",options:{destination:2}}});function fs(t){return t.startsWith("http://")||t.startsWith("https://")}function gs(t){let e=t.replace(/\/+$/,"");try{let n=new URL(e),r=n.pathname.replace(/\/+$/,"");return {origin:`${n.protocol}//${n.host}`,path:r}}catch{return {origin:e,path:""}}}var se=class{constructor(e,n={}){this.destination=e;switch(this.httpsAgent=Qt(n),this.requestTimeoutMs=n.requestTimeoutMs??ms,e.auth.type){case "oauth2":this.tokenManager=new re(e.auth.tokenServiceUrl,e.auth.clientId,e.auth.clientSecret);break;case "ias":this.tokenManager=new Se({issuerUrl:e.auth.tenantUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new K);break;case "xsuaa":this.tokenManager=new Se({issuerUrl:e.auth.xsuaaUrl,clientId:e.auth.clientId,clientSecret:e.auth.clientSecret},new K);break;case "btp-principal":this.tokenManager=new He({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 Be({tokenServiceUrl:e.auth.tokenServiceUrl,clientId:e.auth.clientId,issuer:e.auth.issuer,nameIdFormat:e.auth.nameIdFormat,privateKey:e.auth.privateKey,certificate:e.auth.certificate,clockSkew:e.auth.clockSkew,assertionLifetime:e.auth.assertionLifetime});break;default:this.tokenManager=null;}}tokenManager;userContext=null;httpsAgent;requestTimeoutMs;async request(e){Le();let n=e.method!=="get",r=ee(),s=Ue(),i={...e.params};this.destination.sapClient&&(i["sap-client"]=this.destination.sapClient);let o=e.userContext??this.userContext??void 0,a=await this.buildAuthHeaders(o),c=this.destination.headers??{},l={};if(n){let p=await this.fetchCsrfToken({...c,...a},e.url);p.token&&(l={"x-csrf-token":p.token},p.cookies&&(l.Cookie=p.cookies));}let u={...c,...a,...l,...e.headers},d={method:e.method,url:this.resolveUrl(e.url),data:e.data,headers:u,params:Object.keys(i).length>0?i:void 0,httpsAgent:this.httpsAgent,timeout:this.effectiveTimeoutMs(),signal:r?.signal,beforeRedirect:p=>{this.httpsAgent&&(p.agent=this.httpsAgent);let f=p.headers??{};for(let[g,y]of Object.entries(u))f[g]||(f[g]=y);}},m={...d.headers};m.Authorization&&(m.Authorization=`${m.Authorization.substring(0,15)}***`),j.info({...s?{correlationId:s,tool:r?.toolName}:{},method:d.method,url:d.url,headers:m,params:d.params,hasAuth:!!d.headers.Authorization||!!d.headers.APIKey},"SAP HTTP request outgoing");try{let p=await Ct(d);if(j.info({...s?{correlationId:s}:{},status:p.status,contentType:p.headers["content-type"]},"SAP HTTP response OK"),typeof p.data=="string"&&p.data.trimStart().startsWith("<!DOCTYPE"))throw new Error("SAP returned an HTML page instead of JSON. This usually means authentication failed or the URL path is invalid.");return {data:p.data,headers:p.headers,status:p.status}}catch(p){if(isAxiosError(p)&&p.response){let f=e.expectedStatuses?.includes(p.response.status)??false;(f?j.info.bind(j):j.error.bind(j))({...s?{correlationId:s}:{},status:p.response.status,statusText:p.response.statusText,contentType:p.response.headers?.["content-type"],url:d.url,...f?{expected:true}:{},dataPreview:typeof p.response.data=="string"?p.response.data.substring(0,200):typeof p.response.data},f?"SAP HTTP response (expected)":"SAP HTTP response ERROR");}else p instanceof Error&&j.error({message:p.message,name:p.name},"SAP HTTP connection error");if(n&&this.isCsrf403(p)){Le();try{let f=await this.fetchCsrfToken({...c,...a},e.url),g={...d.headers};f.token&&(g["x-csrf-token"]=f.token,f.cookies&&(g.Cookie=f.cookies)),Ue();let y=await Ct({...d,headers:g});return {data:y.data,headers:y.headers,status:y.status}}catch(f){throw this.toSapError(f)}}throw this.toSapError(p)}}async buildAuthHeaders(e){switch(this.destination.auth.type){case "basic":return {Authorization:`Basic ${Buffer.from(`${this.destination.auth.username}:${this.destination.auth.password}`).toString("base64")}`};case "saml-bearer":case "btp-principal":case "oauth2":case "ias":case "xsuaa":return {Authorization:`Bearer ${await this.tokenManager.getToken(e?.token)}`};case "apikey":return {APIKey:this.destination.auth.apiKey};default:return {}}}resolveUrl(e){if(fs(e))return e;let{origin:n,path:r}=gs(this.destination.url);return r&&(e===r||e.startsWith(`${r}/`))?`${n}${e}`:`${this.destination.url}${e}`}async fetchCsrfToken(e,n){let r=this.destination.url,s=[];if(n){let i=n.replace(/\/[^/]*$/,"/");i!=="/"&&i!==n&&s.push(this.resolveUrl(i));}s.push(`${r}/`,r);for(let i of s){Le(),Ue();try{let o={...e,"x-csrf-token":"fetch"},a={};this.destination.sapClient&&(a["sap-client"]=this.destination.sapClient);let c=await Ct.get(i,{headers:o,params:Object.keys(a).length>0?a:void 0,validateStatus:()=>!0,timeout:this.effectiveTimeoutMs(),signal:ee()?.signal,httpsAgent:this.httpsAgent,beforeRedirect:u=>{this.httpsAgent&&(u.agent=this.httpsAgent);let d=u.headers??{};for(let[m,p]of Object.entries(o))d[m]||(d[m]=p);}}),l=c.headers["x-csrf-token"];if(j.debug({url:i,status:c.status,hasToken:!!l,tokenValue:l?.substring(0,8)},"CSRF token fetch result"),l&&l.toLowerCase()!=="required"){let d=(c.headers["set-cookie"]??[]).map(m=>m.split(";")[0]).join("; ");return {token:l,cookies:d}}}catch{}}return {token:"",cookies:""}}isCsrf403(e){return isAxiosError(e)&&e.response?.status===403}effectiveTimeoutMs(){let e=Gt();return e===void 0?this.requestTimeoutMs:Math.max(1,Math.min(this.requestTimeoutMs,e))}toSapError(e){if(e instanceof D)return {code:e.timedOut?"TOOL_TIMEOUT":"ABORTED",message:e.message,details:[],rawResponse:void 0};if(isAxiosError(e)&&(e.code==="ERR_CANCELED"||e.name==="CanceledError")){let n=ee()?.signal?.reason?.name==="TimeoutError";return {code:n?"TOOL_TIMEOUT":"ABORTED",message:n?"Zeitbudget des Tool-Aufrufs \xFCberschritten \u2014 laufender SAP-Aufruf abgebrochen.":"Client hat die Verbindung verworfen \u2014 laufender SAP-Aufruf abgebrochen.",details:[],rawResponse:void 0}}return isAxiosError(e)&&(e.code==="ECONNABORTED"||e.code==="ETIMEDOUT")?{code:"TIMEOUT",message:`SAP request exceeded ${this.requestTimeoutMs} ms (SAP_HTTP_TIMEOUT_MS): ${e.message}`,details:[],rawResponse:void 0}:isAxiosError(e)&&e.response?te(e.response.data):e instanceof Error?{code:"CONNECTION_ERROR",message:e.message,details:[],rawResponse:void 0}:te(e)}};function Zt(t,e){e||t.warn({sapClient:null},'SAP_CLIENT ist nicht gesetzt. Auf mandantenabh\xE4ngigen Systemen antwortet das Gateway ohne sap-client mit 401 \u201EAnmeldung fehlgeschlagen" \u2014 was wie ein falsches Passwort aussieht, aber keines ist. Bei Anmeldeproblemen zuerst SAP_CLIENT setzen (z. B. 100).');}async function en(t,e,n,r){try{let s=new se(Me(e),n),i=await je(s,{baseUrl:e.baseUrl,sapClient:e.sapClient,tlsVerify:n.tlsVerify,...r?{deps:r}:{}});for(let o of i.stages){let a={stage:o.stage,status:o.status,durationMs:o.durationMs,...o.info??{},...o.guidance?{guidance:o.guidance}:{}};o.status==="failed"?t.warn(a,`Selbsttest ${o.stage}: ${o.detail}`):t.info(a,`Selbsttest ${o.stage}: ${o.detail}`);}i.status==="connected"?t.info({durationMs:i.durationMs,sapClient:e.sapClient??null},"Selbsttest bestanden: Namensaufl\xF6sung, TLS, Anmeldung, Mandant und Katalog sind in Ordnung"):t.warn({failedStage:i.failedStage,durationMs:i.durationMs},`Selbsttest gescheitert in Stufe \u201E${i.failedStage}". Der Server l\xE4uft weiter \u2014 Tool-Aufrufe werden bis zur Behebung fehlschlagen. Einzelheiten liefert das Tool test-connection.`);}catch(s){t.warn({err:s instanceof Error?s.message:String(s)},"Selbsttest konnte nicht ausgef\xFChrt werden. Der Server l\xE4uft weiter.");}}var G=z$1.object({name:z$1.string().default("default"),baseUrl:z$1.url(),sapClient:z$1.string().optional()}),hs=G.extend({authType:z$1.literal("basic"),username:z$1.string().min(1),password:z$1.string().min(1)}),ys=G.extend({authType:z$1.literal("oauth2"),clientId:z$1.string().min(1),clientSecret:z$1.string().min(1),tokenServiceUrl:z$1.url()}),Ss=G.extend({authType:z$1.literal("apikey"),apiKey:z$1.string().min(1)}),vs=G.extend({authType:z$1.literal("ias"),tenantUrl:z$1.url(),clientId:z$1.string().min(1),clientSecret:z$1.string().min(1)}),bs=G.extend({authType:z$1.literal("xsuaa"),xsuaaUrl:z$1.url(),clientId:z$1.string().min(1),clientSecret:z$1.string().min(1)}),Ts=G.extend({authType:z$1.literal("btp-principal"),destinationName:z$1.string().min(1),destinationServiceUrl:z$1.url(),clientId:z$1.string().min(1),clientSecret:z$1.string().min(1),tokenServiceUrl:z$1.url()}),ws=G.extend({authType:z$1.literal("saml-bearer"),tokenServiceUrl:z$1.url(),clientId:z$1.string().min(1),issuer:z$1.string().min(1),nameIdFormat:z$1.enum(["email","sapUsername"]).default("email"),privateKey:z$1.string().min(1),certificate:z$1.string().min(1),clockSkew:z$1.coerce.number().int().nonnegative().default(120),assertionLifetime:z$1.coerce.number().int().positive().default(300)}),ie=z$1.discriminatedUnion("authType",[hs,ys,Ss,vs,bs,Ts,ws]);function be(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=ie.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 _t=/^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/,ws=z$1.enum(["core","odata","idoc","rfc"]),As=z$1.object({readOnly:z$1.boolean().optional(),tiers:z$1.array(ws).min(1).optional()}).strict(),Cs=z$1.object({hash:z$1.string().regex(/^sha256:[0-9a-f]{64}$/,"muss die Form sha256:<64 hex> haben"),destination:z$1.string().min(1),label:z$1.string().max(200).optional(),policy:As.optional(),createdAt:z$1.string().optional(),revoked:z$1.boolean().optional()}).strict(),_s=z$1.object({version:z$1.literal(1),destinations:z$1.record(z$1.string(),z$1.unknown()),tokens:z$1.array(Cs).default([])}).strict(),tn=/\$\{([A-Z_][A-Z0-9_]*)\}/g;function Et(t,e){let n=[],r=s=>{if(typeof s=="string"){let i=[...s.matchAll(tn)].map(a=>a[1]);if(i.length===0)return s;let o=i.filter(a=>e[a]===void 0);if(o.length>0){for(let a of o)n.includes(a)||n.push(a);return s}return s.replace(tn,(a,c)=>e[c]??"")}return Array.isArray(s)?s.map(r):s!==null&&typeof s=="object"?Object.fromEntries(Object.entries(s).map(([i,o])=>[i,r(o)])):s};return {value:r(t),missing:n}}var Es=se.options.map(t=>t.shape.authType.value);function nn(t,e){return e.map(n=>{let r=n.path.join("."),s=r==="authType"?`fehlt oder ist unbekannt (erlaubt: ${Es.join(", ")})`:n.message;return `${t}${r?`${r}: `:""}${s}`})}function Ve(t,e){if(t===null||typeof t!="object"||Array.isArray(t))return {ok:false,errors:['Die Datei muss ein JSON-Objekt mit "version" und "destinations" sein.']};let n=_s.safeParse(t);if(!n.success)return {ok:false,errors:nn("",n.error.issues)};let r=[],s=new Map,i=Object.entries(n.data.destinations);i.length===0&&r.push('"destinations" enth\xE4lt keinen Eintrag.');for(let[a,c]of i){if(!_t.test(a)){r.push(`Destination "${a}": unzul\xE4ssiger Name \u2014 erlaubt sind Buchstaben, Ziffern, "_", "." und "-" (max. 64 Zeichen, kein Leerzeichen).`);continue}if(c===null||typeof c!="object"||Array.isArray(c)){r.push(`Destination "${a}": muss ein Objekt sein.`);continue}let l=Et(c,e);if(l.missing.length>0){r.push(`Destination "${a}": Umgebungsvariable(n) nicht gesetzt: ${l.missing.join(", ")}.`);continue}let u={...l.value,name:a},d=se.safeParse(u);if(!d.success){r.push(...nn(`Destination "${a}": `,d.error.issues));continue}s.set(a,d.data);}let o=n.data.tokens;return o.forEach((a,c)=>{Object.hasOwn(n.data.destinations,a.destination)||r.push(`tokens[${c}]${a.label?` ("${a.label}")`:""}: Destination "${a.destination}" existiert nicht.`);}),r.length>0?{ok:false,errors:r}:{ok:true,value:{destinations:s,tokens:o}}}function be(t,e){let n=createHash("sha256").update(t).digest(),r=createHash("sha256").update(e).digest();return timingSafeEqual(n,r)}var Os="gsm",sn=10,qe=6e4,Ns=1e4;function on(t){return `sha256:${createHash("sha256").update(t,"utf8").digest("hex")}`}function an(t){let e=randomBytes(32).toString("base64url"),n=`${Os}_${t}_${e}`;return {token:n,hash:on(n)}}var We=class{apiKey;tokens;now;failures=new Map;constructor(e){this.apiKey=e.apiKey,this.tokens=e.tokens,this.now=e.now??Date.now;}enforced(){return this.apiKey!==void 0||this.tokens().length>0}authenticate(e,n){if(!this.enforced())return {kind:"open"};let r=this.blockedFor(n);if(r!==void 0)return {kind:"rate-limited",retryAfterSeconds:r};let s=this.check(e);return s.kind==="rejected"?this.recordFailure(n):this.failures.delete(n),s}check(e){let n=Ds(e),r=n??"",s=this.apiKey!==void 0&&be(r,this.apiKey),i=Buffer.from(on(r),"utf8"),o,a=false;for(let c of this.tokens()){let l=Buffer.from(c.hash,"utf8");l.length===i.length&&timingSafeEqual(l,i)&&(c.revoked?a=true:o||(o=c));}return o?{kind:"token",token:o}:s?{kind:"api-key"}:n===void 0?{kind:"rejected",reason:"Kein Bearer-Token im Authorization-Header."}:a?{kind:"rejected",reason:"Das Token wurde widerrufen."}:{kind:"rejected",reason:"Unbekanntes Token oder falscher API-Key."}}blockedFor(e){let n=this.failures.get(e);if(!n)return;let r=this.now()-n.windowStart;if(r>qe){this.failures.delete(e);return}if(n.count>=sn)return Math.max(1,Math.ceil((qe-r)/1e3))}recordFailure(e){let n=this.now();if(this.failures.size>=Ns)for(let[s,i]of this.failures)n-i.windowStart>qe&&this.failures.delete(s);let r=this.failures.get(e);if(!r||n-r.windowStart>qe){this.failures.set(e,{count:1,windowStart:n});return}r.count+=1,r.count===sn&&(r.windowStart=n);}};function Ds(t){return typeof t!="string"?void 0:/^Bearer\s+(.+)$/i.exec(t.trim())?.[1]?.trim()||void 0}var ln=new Set(["destinations","tokens"]),cn=["core","odata","idoc","rfc"],ze=`Usage:
8
+ Set the required SAP_* environment variables.`)}return r.data}var _t=/^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/,As=z$1.enum(["core","odata","idoc","rfc"]),Cs=z$1.object({readOnly:z$1.boolean().optional(),tiers:z$1.array(As).min(1).optional()}).strict(),_s=z$1.object({hash:z$1.string().regex(/^sha256:[0-9a-f]{64}$/,"muss die Form sha256:<64 hex> haben"),destination:z$1.string().min(1),label:z$1.string().max(200).optional(),policy:Cs.optional(),createdAt:z$1.string().optional(),revoked:z$1.boolean().optional()}).strict(),Es=z$1.object({version:z$1.literal(1),destinations:z$1.record(z$1.string(),z$1.unknown()),tokens:z$1.array(_s).default([])}).strict(),tn=/\$\{([A-Z_][A-Z0-9_]*)\}/g;function Et(t,e){let n=[],r=s=>{if(typeof s=="string"){let i=[...s.matchAll(tn)].map(a=>a[1]);if(i.length===0)return s;let o=i.filter(a=>e[a]===void 0);if(o.length>0){for(let a of o)n.includes(a)||n.push(a);return s}return s.replace(tn,(a,c)=>e[c]??"")}return Array.isArray(s)?s.map(r):s!==null&&typeof s=="object"?Object.fromEntries(Object.entries(s).map(([i,o])=>[i,r(o)])):s};return {value:r(t),missing:n}}var Rs=ie.options.map(t=>t.shape.authType.value);function nn(t,e){return e.map(n=>{let r=n.path.join("."),s=r==="authType"?`fehlt oder ist unbekannt (erlaubt: ${Rs.join(", ")})`:n.message;return `${t}${r?`${r}: `:""}${s}`})}function Ve(t,e){if(t===null||typeof t!="object"||Array.isArray(t))return {ok:false,errors:['Die Datei muss ein JSON-Objekt mit "version" und "destinations" sein.']};let n=Es.safeParse(t);if(!n.success)return {ok:false,errors:nn("",n.error.issues)};let r=[],s=new Map,i=Object.entries(n.data.destinations);i.length===0&&r.push('"destinations" enth\xE4lt keinen Eintrag.');for(let[a,c]of i){if(!_t.test(a)){r.push(`Destination "${a}": unzul\xE4ssiger Name \u2014 erlaubt sind Buchstaben, Ziffern, "_", "." und "-" (max. 64 Zeichen, kein Leerzeichen).`);continue}if(c===null||typeof c!="object"||Array.isArray(c)){r.push(`Destination "${a}": muss ein Objekt sein.`);continue}let l=Et(c,e);if(l.missing.length>0){r.push(`Destination "${a}": Umgebungsvariable(n) nicht gesetzt: ${l.missing.join(", ")}.`);continue}let u={...l.value,name:a},d=ie.safeParse(u);if(!d.success){r.push(...nn(`Destination "${a}": `,d.error.issues));continue}s.set(a,d.data);}let o=n.data.tokens;return o.forEach((a,c)=>{Object.hasOwn(n.data.destinations,a.destination)||r.push(`tokens[${c}]${a.label?` ("${a.label}")`:""}: Destination "${a.destination}" existiert nicht.`);}),r.length>0?{ok:false,errors:r}:{ok:true,value:{destinations:s,tokens:o}}}function Te(t,e){let n=createHash("sha256").update(t).digest(),r=createHash("sha256").update(e).digest();return timingSafeEqual(n,r)}var Ns="gsm",sn=10,qe=6e4,Ds=1e4;function on(t){return `sha256:${createHash("sha256").update(t,"utf8").digest("hex")}`}function an(t){let e=randomBytes(32).toString("base64url"),n=`${Ns}_${t}_${e}`;return {token:n,hash:on(n)}}var We=class{apiKey;tokens;now;failures=new Map;constructor(e){this.apiKey=e.apiKey,this.tokens=e.tokens,this.now=e.now??Date.now;}enforced(){return this.apiKey!==void 0||this.tokens().length>0}authenticate(e,n){if(!this.enforced())return {kind:"open"};let r=this.blockedFor(n);if(r!==void 0)return {kind:"rate-limited",retryAfterSeconds:r};let s=this.check(e);return s.kind==="rejected"?this.recordFailure(n):this.failures.delete(n),s}check(e){let n=xs(e),r=n??"",s=this.apiKey!==void 0&&Te(r,this.apiKey),i=Buffer.from(on(r),"utf8"),o,a=false;for(let c of this.tokens()){let l=Buffer.from(c.hash,"utf8");l.length===i.length&&timingSafeEqual(l,i)&&(c.revoked?a=true:o||(o=c));}return o?{kind:"token",token:o}:s?{kind:"api-key"}:n===void 0?{kind:"rejected",reason:"Kein Bearer-Token im Authorization-Header."}:a?{kind:"rejected",reason:"Das Token wurde widerrufen."}:{kind:"rejected",reason:"Unbekanntes Token oder falscher API-Key."}}blockedFor(e){let n=this.failures.get(e);if(!n)return;let r=this.now()-n.windowStart;if(r>qe){this.failures.delete(e);return}if(n.count>=sn)return Math.max(1,Math.ceil((qe-r)/1e3))}recordFailure(e){let n=this.now();if(this.failures.size>=Ds)for(let[s,i]of this.failures)n-i.windowStart>qe&&this.failures.delete(s);let r=this.failures.get(e);if(!r||n-r.windowStart>qe){this.failures.set(e,{count:1,windowStart:n});return}r.count+=1,r.count===sn&&(r.windowStart=n);}};function xs(t){return typeof t!="string"?void 0:/^Bearer\s+(.+)$/i.exec(t.trim())?.[1]?.trim()||void 0}var ln=new Set(["destinations","tokens"]),cn=["core","odata","idoc","rfc"],ze=`Usage:
9
9
  guniweb-sap-mcp destinations list
10
10
  guniweb-sap-mcp destinations add <name> (--from-env | --base-url <url> --auth-type <type> [fields])
11
11
  [--sap-client <c>] [--set <field>=<value>]... [--replace]
@@ -28,28 +28,28 @@ Auth types: basic, oauth2, apikey, ias, xsuaa, btp-principal, saml-bearer.
28
28
 
29
29
  "tokens issue" prints the plain token ONCE on stdout; only its SHA-256 hash is stored.
30
30
  Put the token into an n8n Bearer Auth credential on the MCP Client node.
31
- `,w=class extends Error{constructor(n,r=1){super(n);this.exitCode=r;}};async function dn(t,e){let n=qs(t);try{if(n.flags.help)return e.stdout(ze),0;let[r,s,...i]=n.positional;if(r==="destinations")switch(s){case "list":return Ws(n,e);case "add":return Ks(i[0],n,e);case "remove":return Gs(i[0],n,e);default:throw new w(`Unknown command "destinations ${s??""}".
31
+ `,w=class extends Error{constructor(n,r=1){super(n);this.exitCode=r;}};async function dn(t,e){let n=Ws(t);try{if(n.flags.help)return e.stdout(ze),0;let[r,s,...i]=n.positional;if(r==="destinations")switch(s){case "list":return zs(n,e);case "add":return Gs(i[0],n,e);case "remove":return Ys(i[0],n,e);default:throw new w(`Unknown command "destinations ${s??""}".
32
32
 
33
- ${ze}`,2)}if(r==="tokens")switch(s){case "list":return Ys(n,e);case "issue":return Js(i[0],n,e);case "revoke":return Xs(i[0],n,e);default:throw new w(`Unknown command "tokens ${s??""}".
33
+ ${ze}`,2)}if(r==="tokens")switch(s){case "list":return Js(n,e);case "issue":return Xs(i[0],n,e);case "revoke":return Qs(i[0],n,e);default:throw new w(`Unknown command "tokens ${s??""}".
34
34
 
35
35
  ${ze}`,2)}throw new w(ze,2)}catch(r){return r instanceof w?(e.stderr(`${r.message}
36
36
  `),r.exitCode):(e.stderr(`Error: ${r instanceof Error?r.message:String(r)}
37
- `),1)}}var Bs=new Set(["help","h","from-env","replace","force","read-only","allow-missing-env"]),Vs=new Set(["set"]);function qs(t){let e=[],n={};for(let r=0;r<t.length;r++){let s=t[r];if(s==="-h"){n.help=true;continue}if(!s.startsWith("--")){e.push(s);continue}let i=s.indexOf("="),o=i===-1?s.slice(2):s.slice(2,i);if(Bs.has(o)){n[o]=true;continue}let a=i===-1?t[++r]:s.slice(i+1);if(a===void 0)throw new w(`Missing value for --${o}.`,2);if(Vs.has(o)){let c=n[o]??[];c.push(a),n[o]=c;}else n[o]=a;}return {positional:e,flags:n}}function Ke(t,e){let n=t.flags[e];return typeof n=="string"?n:void 0}function oe(t,e){let n=Ke(t,"destinations")??e.env.SAP_MCP_DESTINATIONS;if(!n||n.trim().length===0)throw new w("No destinations file given. Pass --destinations <path> or set SAP_MCP_DESTINATIONS.",2);return resolve(n.trim())}function ae(t,{mustExist:e}){if(!existsSync(t)){if(e)throw new w(`${t}: file not found.`);return {version:1,destinations:{},tokens:[]}}let n;try{n=JSON.parse(readFileSync(t,"utf8"));}catch(s){throw new w(`${t}: not valid JSON (${s instanceof Error?s.message:String(s)}).`)}if(n===null||typeof n!="object"||Array.isArray(n))throw new w(`${t}: must be a JSON object with "version" and "destinations".`);let r=n;return {version:1,destinations:r.destinations??{},tokens:r.tokens??[],...r.version!==void 0&&r.version!==1?{version:r.version}:{}}}function ce(t,e,n){let r=Et(t.destinations,e),s=[],i=e;if(r.missing.length>0&&n){i={...e};for(let a of r.missing)i[a]="placeholder";s.push(`Environment variable(s) not set here: ${r.missing.join(", ")}. The server needs them at load time.`);}let o=Ve(t,i);if(!o.ok)throw new w(`Refusing to write an invalid file:
37
+ `),1)}}var Vs=new Set(["help","h","from-env","replace","force","read-only","allow-missing-env"]),qs=new Set(["set"]);function Ws(t){let e=[],n={};for(let r=0;r<t.length;r++){let s=t[r];if(s==="-h"){n.help=true;continue}if(!s.startsWith("--")){e.push(s);continue}let i=s.indexOf("="),o=i===-1?s.slice(2):s.slice(2,i);if(Vs.has(o)){n[o]=true;continue}let a=i===-1?t[++r]:s.slice(i+1);if(a===void 0)throw new w(`Missing value for --${o}.`,2);if(qs.has(o)){let c=n[o]??[];c.push(a),n[o]=c;}else n[o]=a;}return {positional:e,flags:n}}function Ke(t,e){let n=t.flags[e];return typeof n=="string"?n:void 0}function ae(t,e){let n=Ke(t,"destinations")??e.env.SAP_MCP_DESTINATIONS;if(!n||n.trim().length===0)throw new w("No destinations file given. Pass --destinations <path> or set SAP_MCP_DESTINATIONS.",2);return resolve(n.trim())}function ce(t,{mustExist:e}){if(!existsSync(t)){if(e)throw new w(`${t}: file not found.`);return {version:1,destinations:{},tokens:[]}}let n;try{n=JSON.parse(readFileSync(t,"utf8"));}catch(s){throw new w(`${t}: not valid JSON (${s instanceof Error?s.message:String(s)}).`)}if(n===null||typeof n!="object"||Array.isArray(n))throw new w(`${t}: must be a JSON object with "version" and "destinations".`);let r=n;return {version:1,destinations:r.destinations??{},tokens:r.tokens??[],...r.version!==void 0&&r.version!==1?{version:r.version}:{}}}function le(t,e,n){let r=Et(t.destinations,e),s=[],i=e;if(r.missing.length>0&&n){i={...e};for(let a of r.missing)i[a]="placeholder";s.push(`Environment variable(s) not set here: ${r.missing.join(", ")}. The server needs them at load time.`);}let o=Ve(t,i);if(!o.ok)throw new w(`Refusing to write an invalid file:
38
38
  ${o.errors.map(a=>` - ${a}`).join(`
39
39
  `)}`);return {warnings:s}}function Ge(t,e){let n=dirname(t);mkdirSync(n,{recursive:true});let r=`${t}.${process.pid}.tmp`,s=`${JSON.stringify(e,null,2)}
40
- `;try{writeFileSync(r,s,{mode:384}),renameSync(r,t);}catch(i){try{unlinkSync(r);}catch{}throw i}}function un(t,e){return t.tokens.filter(n=>n.destination===e)}function Ws(t,e){let n=oe(t,e),r=ae(n,{mustExist:true});ce(r,e.env,true);let s=Object.entries(r.destinations).map(([i,o])=>({name:i,baseUrl:String(o.baseUrl??""),authType:String(o.authType??""),sapClient:o.sapClient===void 0?"-":String(o.sapClient),tokens:String(un(r,i).length)}));return e.stdout(`${pn(["NAME","BASE URL","AUTH","CLIENT","TOKENS"],s.map(i=>[i.name,i.baseUrl,i.authType,i.sapClient,i.tokens]))}
41
- `),0}var zs={"base-url":"baseUrl","auth-type":"authType","sap-client":"sapClient",username:"username",password:"password","api-key":"apiKey","client-id":"clientId","client-secret":"clientSecret","token-service-url":"tokenServiceUrl"};function Ks(t,e,n){if(!t)throw new w("Usage: destinations add <name> ...",2);if(!_t.test(t))throw new w(`"${t}" is not a valid destination name (letters, digits, "_", ".", "-", max 64 characters).`);let r=oe(e,n),s=ae(r,{mustExist:false});if(s.destinations[t]&&!e.flags.replace)throw new w(`Destination "${t}" already exists. Use --replace to overwrite it.`);let i;if(e.flags["from-env"]){let{name:c,...l}=Te(n.env);i=Qs(l);}else {i={};for(let[c,l]of Object.entries(zs)){let u=Ke(e,c);u!==void 0&&(i[l]=u);}for(let c of e.flags.set??[]){let l=c.indexOf("=");if(l<=0)throw new w(`--set expects field=value, got "${c}".`,2);i[c.slice(0,l)]=c.slice(l+1);}if(Object.keys(i).length===0)throw new w("Nothing to add: pass --from-env or --base-url/--auth-type and the auth fields.",2)}let o={...s,destinations:{...s.destinations,[t]:i}},{warnings:a}=ce(o,n.env,!!e.flags["allow-missing-env"]);Ge(r,o);for(let c of a)n.stderr(`Warning: ${c}
40
+ `;try{writeFileSync(r,s,{mode:384}),renameSync(r,t);}catch(i){try{unlinkSync(r);}catch{}throw i}}function un(t,e){return t.tokens.filter(n=>n.destination===e)}function zs(t,e){let n=ae(t,e),r=ce(n,{mustExist:true});le(r,e.env,true);let s=Object.entries(r.destinations).map(([i,o])=>({name:i,baseUrl:String(o.baseUrl??""),authType:String(o.authType??""),sapClient:o.sapClient===void 0?"-":String(o.sapClient),tokens:String(un(r,i).length)}));return e.stdout(`${pn(["NAME","BASE URL","AUTH","CLIENT","TOKENS"],s.map(i=>[i.name,i.baseUrl,i.authType,i.sapClient,i.tokens]))}
41
+ `),0}var Ks={"base-url":"baseUrl","auth-type":"authType","sap-client":"sapClient",username:"username",password:"password","api-key":"apiKey","client-id":"clientId","client-secret":"clientSecret","token-service-url":"tokenServiceUrl"};function Gs(t,e,n){if(!t)throw new w("Usage: destinations add <name> ...",2);if(!_t.test(t))throw new w(`"${t}" is not a valid destination name (letters, digits, "_", ".", "-", max 64 characters).`);let r=ae(e,n),s=ce(r,{mustExist:false});if(s.destinations[t]&&!e.flags.replace)throw new w(`Destination "${t}" already exists. Use --replace to overwrite it.`);let i;if(e.flags["from-env"]){let{name:c,...l}=be(n.env);i=Zs(l);}else {i={};for(let[c,l]of Object.entries(Ks)){let u=Ke(e,c);u!==void 0&&(i[l]=u);}for(let c of e.flags.set??[]){let l=c.indexOf("=");if(l<=0)throw new w(`--set expects field=value, got "${c}".`,2);i[c.slice(0,l)]=c.slice(l+1);}if(Object.keys(i).length===0)throw new w("Nothing to add: pass --from-env or --base-url/--auth-type and the auth fields.",2)}let o={...s,destinations:{...s.destinations,[t]:i}},{warnings:a}=le(o,n.env,!!e.flags["allow-missing-env"]);Ge(r,o);for(let c of a)n.stderr(`Warning: ${c}
42
42
  `);return n.stderr(`Destination "${t}" ${s.destinations[t]?"replaced":"added"} in ${r}.
43
- `),0}function Gs(t,e,n){if(!t)throw new w("Usage: destinations remove <name> [--force]",2);let r=oe(e,n),s=ae(r,{mustExist:true});if(!s.destinations[t])throw new w(`Destination "${t}" not found in ${r}.`);let i=un(s,t);if(i.length>0&&!e.flags.force)throw new w(`Destination "${t}" still has ${i.length} token(s) (${i.map(l=>typeof l.label=="string"?l.label:ie(l.hash)).join(", ")}). Revoke them first, or pass --force to remove them together.`);let{[t]:o,...a}=s.destinations,c={...s,destinations:a,tokens:s.tokens.filter(l=>l.destination!==t)};return ce(c,n.env,true),Ge(r,c),n.stderr(`Destination "${t}" removed${i.length>0?` together with ${i.length} token(s)`:""}.
44
- `),0}function Ys(t,e){let n=oe(t,e),r=ae(n,{mustExist:true});ce(r,e.env,true);let s=r.tokens.map(i=>{let o=i.policy,a=[];return o?.readOnly&&a.push("read-only"),o?.tiers&&a.push(`tiers=${o.tiers.join(",")}`),[typeof i.label=="string"?i.label:"-",String(i.destination),ie(i.hash),a.length>0?a.join(" "):"full",i.revoked?"revoked":"active",typeof i.createdAt=="string"?i.createdAt:"-"]});return e.stdout(`${pn(["LABEL","DESTINATION","HASH","POLICY","STATUS","CREATED"],s)}
45
- `),0}function Js(t,e,n){if(!t)throw new w("Usage: tokens issue <destination> [--label ...]",2);let r=oe(e,n),s=ae(r,{mustExist:true});if(!s.destinations[t])throw new w(`Destination "${t}" not found in ${r}. Known: ${Object.keys(s.destinations).join(", ")||"(none)"}.`);let i={};e.flags["read-only"]&&(i.readOnly=true);let o=Ke(e,"tiers");if(o!==void 0){let m=o.split(",").map(f=>f.trim()).filter(f=>f.length>0),p=m.filter(f=>!cn.includes(f));if(p.length>0||m.length===0)throw new w(`Unknown tier(s): ${p.join(", ")||"(empty)"}. Allowed: ${cn.join(", ")}.`);i.tiers=m;}let{token:a,hash:c}=an(t),l=Ke(e,"label"),u={hash:c,destination:t,...l?{label:l}:{},...Object.keys(i).length>0?{policy:i}:{},createdAt:(n.now??(()=>new Date))().toISOString()},d={...s,tokens:[...s.tokens,u]};return ce(d,n.env,true),Ge(r,d),n.stdout(`${a}
46
- `),n.stderr([`Token issued for destination "${t}"${l?` (${l})`:""} \u2014 hash ${ie(c)}\u2026 stored in ${r}.`,"The plain token above is shown ONCE and cannot be recovered; put it into an n8n Bearer Auth credential","(Authorization: Bearer <token>) on the MCP Client node. Revoke with: tokens revoke <label|hash-prefix>.",""].join(`
47
- `)),0}function Xs(t,e,n){if(!t)throw new w("Usage: tokens revoke <label | hash-prefix>",2);let r=oe(e,n),s=ae(r,{mustExist:true}),i=s.tokens.filter(d=>d.label===t),o=t.startsWith("sha256:")?t.slice(7):t,a=/^[0-9a-f]{8,64}$/i.test(o)?s.tokens.filter(d=>typeof d.hash=="string"&&d.hash.slice(7).startsWith(o.toLowerCase())):[],c=i.length>0?i:a;if(c.length===0)throw new w(`No token matches "${t}". Use the label or at least 8 hex characters of the hash (see: tokens list).`);if(c.length>1)throw new w(`"${t}" is ambiguous (${c.length} tokens). Use a longer hash prefix: ${c.map(d=>ie(d.hash)).join(", ")}.`);let l=c[0];if(l.revoked===true)return n.stderr(`Token ${ie(l.hash)} was already revoked.
48
- `),0;let u={...s,tokens:s.tokens.map(d=>d===l?{...d,revoked:true}:d)};return ce(u,n.env,true),Ge(r,u),n.stderr(`Token ${ie(l.hash)}${typeof l.label=="string"?` (${l.label})`:""} revoked. A running server rejects it from the next request on.
49
- `),0}function ie(t){return typeof t=="string"?t.replace(/^sha256:/,"").slice(0,12):"?"}function Qs(t){return Object.fromEntries(Object.entries(t).filter(([,e])=>e!==void 0))}function pn(t,e){let n=[t,...e],r=t.map((s,i)=>Math.max(...n.map(o=>(o[i]??"").length)));return n.map(s=>s.map((i,o)=>(i??"").padEnd(r[o])).join(" ").trimEnd()).join(`
50
- `)}var le="default",ii=250,oi=1e3,we=class t{source;filePath;current;currentRaw;currentVersion=1;env;envDefault;warn;observeFile;warnedFileWins=false;watcher;polling=false;sighupHandler;debounceTimer;reloadInFlight;reloadQueued=false;changeListeners=new Set;errorListeners=new Set;constructor(e){this.source=e.source,this.filePath=e.filePath,this.current=e.config,this.currentRaw=e.raw,this.env=e.env,this.envDefault=e.envDefault,this.warn=e.onWarn??(()=>{}),this.observeFile=e.watchFile??true;}static fromEnv(e=process.env){let n={...Te(e),name:le};return new t({source:"env",config:{destinations:new Map([[le,n]]),tokens:[]},env:e})}static async fromFile(e,n={}){let r=n.env??process.env,s=resolve(e),i=r.SAP_BASE_URL?{...Te(r),name:le}:void 0,o=new t({source:"file",filePath:s,config:{destinations:new Map,tokens:[]},env:r,envDefault:i,onWarn:n.onWarn,watchFile:n.watchFile}),a=await o.readAndParse();if(!a.ok)throw new Error(`Ung\xFCltige Destinations-Datei ${s}:
43
+ `),0}function Ys(t,e,n){if(!t)throw new w("Usage: destinations remove <name> [--force]",2);let r=ae(e,n),s=ce(r,{mustExist:true});if(!s.destinations[t])throw new w(`Destination "${t}" not found in ${r}.`);let i=un(s,t);if(i.length>0&&!e.flags.force)throw new w(`Destination "${t}" still has ${i.length} token(s) (${i.map(l=>typeof l.label=="string"?l.label:oe(l.hash)).join(", ")}). Revoke them first, or pass --force to remove them together.`);let{[t]:o,...a}=s.destinations,c={...s,destinations:a,tokens:s.tokens.filter(l=>l.destination!==t)};return le(c,n.env,true),Ge(r,c),n.stderr(`Destination "${t}" removed${i.length>0?` together with ${i.length} token(s)`:""}.
44
+ `),0}function Js(t,e){let n=ae(t,e),r=ce(n,{mustExist:true});le(r,e.env,true);let s=r.tokens.map(i=>{let o=i.policy,a=[];return o?.readOnly&&a.push("read-only"),o?.tiers&&a.push(`tiers=${o.tiers.join(",")}`),[typeof i.label=="string"?i.label:"-",String(i.destination),oe(i.hash),a.length>0?a.join(" "):"full",i.revoked?"revoked":"active",typeof i.createdAt=="string"?i.createdAt:"-"]});return e.stdout(`${pn(["LABEL","DESTINATION","HASH","POLICY","STATUS","CREATED"],s)}
45
+ `),0}function Xs(t,e,n){if(!t)throw new w("Usage: tokens issue <destination> [--label ...]",2);let r=ae(e,n),s=ce(r,{mustExist:true});if(!s.destinations[t])throw new w(`Destination "${t}" not found in ${r}. Known: ${Object.keys(s.destinations).join(", ")||"(none)"}.`);let i={};e.flags["read-only"]&&(i.readOnly=true);let o=Ke(e,"tiers");if(o!==void 0){let m=o.split(",").map(f=>f.trim()).filter(f=>f.length>0),p=m.filter(f=>!cn.includes(f));if(p.length>0||m.length===0)throw new w(`Unknown tier(s): ${p.join(", ")||"(empty)"}. Allowed: ${cn.join(", ")}.`);i.tiers=m;}let{token:a,hash:c}=an(t),l=Ke(e,"label"),u={hash:c,destination:t,...l?{label:l}:{},...Object.keys(i).length>0?{policy:i}:{},createdAt:(n.now??(()=>new Date))().toISOString()},d={...s,tokens:[...s.tokens,u]};return le(d,n.env,true),Ge(r,d),n.stdout(`${a}
46
+ `),n.stderr([`Token issued for destination "${t}"${l?` (${l})`:""} \u2014 hash ${oe(c)}\u2026 stored in ${r}.`,"The plain token above is shown ONCE and cannot be recovered; put it into an n8n Bearer Auth credential","(Authorization: Bearer <token>) on the MCP Client node. Revoke with: tokens revoke <label|hash-prefix>.",""].join(`
47
+ `)),0}function Qs(t,e,n){if(!t)throw new w("Usage: tokens revoke <label | hash-prefix>",2);let r=ae(e,n),s=ce(r,{mustExist:true}),i=s.tokens.filter(d=>d.label===t),o=t.startsWith("sha256:")?t.slice(7):t,a=/^[0-9a-f]{8,64}$/i.test(o)?s.tokens.filter(d=>typeof d.hash=="string"&&d.hash.slice(7).startsWith(o.toLowerCase())):[],c=i.length>0?i:a;if(c.length===0)throw new w(`No token matches "${t}". Use the label or at least 8 hex characters of the hash (see: tokens list).`);if(c.length>1)throw new w(`"${t}" is ambiguous (${c.length} tokens). Use a longer hash prefix: ${c.map(d=>oe(d.hash)).join(", ")}.`);let l=c[0];if(l.revoked===true)return n.stderr(`Token ${oe(l.hash)} was already revoked.
48
+ `),0;let u={...s,tokens:s.tokens.map(d=>d===l?{...d,revoked:true}:d)};return le(u,n.env,true),Ge(r,u),n.stderr(`Token ${oe(l.hash)}${typeof l.label=="string"?` (${l.label})`:""} revoked. A running server rejects it from the next request on.
49
+ `),0}function oe(t){return typeof t=="string"?t.replace(/^sha256:/,"").slice(0,12):"?"}function Zs(t){return Object.fromEntries(Object.entries(t).filter(([,e])=>e!==void 0))}function pn(t,e){let n=[t,...e],r=t.map((s,i)=>Math.max(...n.map(o=>(o[i]??"").length)));return n.map(s=>s.map((i,o)=>(i??"").padEnd(r[o])).join(" ").trimEnd()).join(`
50
+ `)}var Y="default",oi=250,mn=1e3,we=class t{source;filePath;current;currentRaw;currentVersion=1;env;envDefault;warn;observeFile;warnedFileWins=false;watcher;polling=false;startupRecheck;sighupHandler;debounceTimer;reloadInFlight;reloadQueued=false;changeListeners=new Set;errorListeners=new Set;constructor(e){this.source=e.source,this.filePath=e.filePath,this.current=e.config,this.currentRaw=e.raw,this.env=e.env,this.envDefault=e.envDefault,this.warn=e.onWarn??(()=>{}),this.observeFile=e.watchFile??true;}static fromEnv(e=process.env){let n={...be(e),name:Y};return new t({source:"env",config:{destinations:new Map([[Y,n]]),tokens:[]},env:e})}static async fromFile(e,n={}){let r=n.env??process.env,s=resolve(e),i=r.SAP_BASE_URL?{...be(r),name:Y}:void 0,o=new t({source:"file",filePath:s,config:{destinations:new Map,tokens:[]},env:r,envDefault:i,onWarn:n.onWarn,watchFile:n.watchFile}),a=await o.readAndParse();if(!a.ok){if(a.missing&&i)return o.warn(`${s} existiert (noch) nicht \u2014 der Server startet mit der SAP_*-Umgebung als Destination "default" und \xFCbernimmt die Datei, sobald sie angelegt wird.`),o.current={destinations:new Map([[Y,i]]),tokens:[]},o;throw new Error(`Ung\xFCltige Destinations-Datei ${s}:
51
51
  ${a.errors.map(c=>` - ${c}`).join(`
52
- `)}`);return o.current=a.value,o.currentRaw=a.raw,o}get version(){return this.currentVersion}get(e){return this.current.destinations.get(e)}names(){return [...this.current.destinations.keys()]}defaultDestination(){let e=this.current.destinations.get(le);if(e)return e;if(this.current.destinations.size===1)return this.current.destinations.values().next().value}tokens(){return this.current.tokens}onChange(e){return this.changeListeners.add(e),()=>this.changeListeners.delete(e)}onError(e){return this.errorListeners.add(e),()=>this.errorListeners.delete(e)}reload(){return this.source==="env"?Promise.resolve({ok:true,changed:false}):this.reloadInFlight?(this.reloadQueued=true,this.reloadInFlight):(this.reloadInFlight=this.doReload().finally(()=>{this.reloadInFlight=void 0,this.reloadQueued&&(this.reloadQueued=false,this.reload());}),this.reloadInFlight)}watch(){if(!(this.source==="env"||!this.filePath)&&(this.sighupHandler||(this.sighupHandler=()=>{this.reload();},process.on("SIGHUP",this.sighupHandler)),this.observeFile&&!this.polling&&(this.polling=true,watchFile(this.filePath,{persistent:false,interval:oi},(e,n)=>{(e.mtimeMs!==n.mtimeMs||e.size!==n.size)&&this.scheduleReload();})),this.observeFile&&!this.watcher))try{this.watcher=watch(dirname(this.filePath),{persistent:!1},()=>{this.scheduleReload();}),this.watcher.on("error",e=>{this.warn(`Dateibeobachtung f\xFCr ${this.filePath} ausgefallen (${e.message}); SIGHUP l\xE4dt weiterhin neu.`),this.watcher=void 0;});}catch(e){this.warn(`Dateibeobachtung f\xFCr ${this.filePath} nicht m\xF6glich (${e instanceof Error?e.message:String(e)}); SIGHUP l\xE4dt weiterhin neu.`);}}close(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=void 0),this.watcher&&(this.watcher.close(),this.watcher=void 0),this.polling&&this.filePath&&(unwatchFile(this.filePath),this.polling=false),this.sighupHandler&&(process.off("SIGHUP",this.sighupHandler),this.sighupHandler=void 0),this.changeListeners.clear(),this.errorListeners.clear();}scheduleReload(){this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.debounceTimer=void 0,this.reload();},ii),this.debounceTimer.unref();}async doReload(){let e=await this.readAndParse();if(!e.ok){for(let r of this.errorListeners)r(e.errors);return {ok:false,errors:e.errors}}if(e.raw===this.currentRaw)return {ok:true,changed:false};this.current=e.value,this.currentRaw=e.raw,this.currentVersion+=1;let n={version:this.currentVersion,names:this.names(),tokenCount:this.current.tokens.length};for(let r of this.changeListeners)r(n);return {ok:true,changed:true}}async readAndParse(){if(!this.filePath)return {ok:false,errors:["Kein Dateipfad konfiguriert."]};let e;try{e=await readFile(this.filePath,"utf8");}catch(s){let o=s.code==="ENOENT"?"Datei nicht gefunden":s.message;return {ok:false,errors:[`${this.filePath}: ${o}`]}}let n;try{n=JSON.parse(e);}catch(s){return {ok:false,errors:[`${this.filePath}: kein g\xFCltiges JSON (${s instanceof Error?s.message:String(s)})`]}}let r=Ve(n,this.env);return r.ok?(this.envDefault&&(r.value.destinations.has(le)?this.warnedFileWins||(this.warnedFileWins=true,this.warn(`Sowohl ${this.filePath} als auch die SAP_*-Umgebung definieren die Destination "default" \u2014 die Datei gilt, die Umgebung wird ignoriert.`)):r.value.destinations.set(le,this.envDefault)),{ok:true,value:r.value,raw:e}):r}};var de=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 ci=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",parseTagValue:false,processEntities:false,htmlEntities:false,isArray:t=>t==="IDOC"||t.startsWith("E1")||t.startsWith("E2")}),Ae=new Set(["@_SEGMENT","@_BEGIN"]);function Rt(t,e){return !!(t.startsWith("E1")||t.startsWith("E2")||typeof e=="object"&&e!==null&&"@_SEGMENT"in e)}function mn(t){let e=[];for(let[n,r]of Object.entries(t)){if(Ae.has(n)||!Rt(n,r))continue;let s=Array.isArray(r)?r:[r];for(let i of s){if(typeof i!="object"||i===null)continue;let o={},a=[];for(let[c,l]of Object.entries(i))if(!Ae.has(c))if(Rt(c,l)){let u=Array.isArray(l)?l:[l];for(let d of u){if(typeof d!="object"||d===null)continue;let m={},p=mn(d);for(let[f,g]of Object.entries(d))Ae.has(f)||!Rt(f,g)&&typeof g!="object"&&(m[f]=String(g));a.push({name:c,fields:m,...p.length>0?{children:p}:{}});}}else typeof l!="object"&&(o[c]=String(l));e.push({name:n,fields:o,...a.length>0?{children:a}:{}});}}return e}function fn(t){let e=ci.parse(t),n=Object.keys(e).find(d=>d!=="?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 d=Array.isArray(a)?a[0]:a;for(let[m,p]of Object.entries(d))!Ae.has(m)&&typeof p!="object"&&(c[m]=String(p));}let l={};for(let[d,m]of Object.entries(o))d==="EDI_DC40"||Ae.has(d)||(l[d]=m);let u=mn(l);return {idocType:r,controlRecord:c,segments:u}}var li=10*1024*1024,Ye=class extends Error{};function di(t,e){return new Promise((n,r)=>{let s=[],i=0,o=false;t.on("data",a=>{if(!o){if(i+=a.length,i>e){o=true,s.length=0,r(new Ye(`Request body exceeds ${e} bytes`));return}s.push(a);}}),t.on("end",()=>{o||n(Buffer.concat(s).toString("utf-8"));}),t.on("error",a=>{o||r(a);});})}function ui(t,e){if(e.length===0||typeof t!="string")return false;if(t.startsWith("Bearer "))return be(t.slice(7),e);if(t.startsWith("Basic ")){let n=Buffer.from(t.slice(6),"base64").toString("utf-8"),r=n.indexOf(":");return r===-1?false:be(n.slice(r+1),e)}return false}function pi(t){if(typeof t!="string")return true;let e=(t.split(";")[0]??"").trim().toLowerCase();return e.endsWith("xml")||e==="application/x-sap.idoc"}function Je(t,e={}){let n=e.maxBodyBytes??li;return (r,s)=>{if(r.method!=="POST"){s.writeHead(405,{"Content-Type":"text/plain"}),s.end("Method Not Allowed");return}if(e.secret!==void 0&&!ui(r.headers.authorization,e.secret)){s.writeHead(401,{"Content-Type":"text/plain","WWW-Authenticate":'Basic realm="idoc-webhook"'}),s.end("Unauthorized"),r.destroy();return}if(!pi(r.headers["content-type"])){s.writeHead(415,{"Content-Type":"text/plain"}),s.end("Unsupported Media Type: expected XML"),r.destroy();return}di(r,n).then(i=>{let{idocType:o,controlRecord:a,segments:c}=fn(i),l={docnum:a.DOCNUM||`UNKNOWN_${Date.now()}`,idocType:o,mesType:a.MESTYP||"",receivedAt:new Date,rawXml:i,parsed:{controlRecord:a,segments:c}};t.add(l),s.writeHead(200,{"Content-Type":"text/plain"}),s.end("OK");}).catch(i=>{if(i instanceof Ye){s.writeHead(413,{"Content-Type":"text/plain"}),s.end("Payload Too Large"),r.destroy();return}s.writeHead(400,{"Content-Type":"text/plain"}),s.end(`Parse error: ${i instanceof Error?i.message:String(i)}`);});}}function gn(t,e,n={}){let r=Je(e,n);return new Promise((s,i)=>{let o=createServer((c,l)=>{c.method==="POST"&&c.url==="/idoc"?r(c,l):(l.writeHead(404,{"Content-Type":"text/plain"}),l.end("Not Found"));}),a=()=>{let c=o.address(),l=typeof c=="object"&&c!==null?c.port:t;s({port:l,close:()=>new Promise((u,d)=>{o.close(m=>m?d(m):u());})});};n.host?o.listen(t,n.host,a):o.listen(t,a),o.on("error",i);})}var yn=1;function Xe(t){if(!t.directory)return [];let e=join(t.directory,t.fileName),n;try{n=readFileSync(e,"utf-8");}catch{return []}try{let r=JSON.parse(n);if(r?.version!==yn||!Array.isArray(r.entries))return t.onWarn?.(`Cache-Datei ${e} hat ein unbekanntes Format und wird verworfen.`),[];let s=Date.now()-t.ttlMs;return r.entries.filter(i=>i&&typeof i.key=="string"&&typeof i.storedAt=="number"&&i.storedAt>s)}catch(r){return t.onWarn?.(`Cache-Datei ${e} ist unlesbar und wird verworfen. Der Cache wird aus SAP neu aufgebaut.`,r),[]}}function Qe(t,e){if(!e.directory)return;let n=join(e.directory,e.fileName),r=`${n}.tmp`;try{mkdirSync(dirname(n),{recursive:!0}),writeFileSync(r,JSON.stringify({version:yn,entries:t}),"utf-8"),renameSync(r,n);}catch(s){e.onWarn?.(`Cache konnte nicht nach ${n} geschrieben werden. Der Betrieb l\xE4uft weiter, nach einem Neustart ist der Cache aber leer.`,s);try{unlinkSync(r);}catch{}}}var Ti=30,bi="catalog-cache.json",Sn="catalog",H=class{cache=null;ttlMs;key;persistence;constructor(e={}){let n=typeof e=="number"?{ttlMinutes:e}:e;this.ttlMs=(n.ttlMinutes??Ti)*60*1e3;let r=(n.baseUrl??"").replace(/\/+$/,"").toLowerCase();if(this.key=r?`${r}|${Sn}`:Sn,n.persistDir){this.persistence={directory:n.persistDir,fileName:bi,ttlMs:this.ttlMs,...n.onWarn?{onWarn:n.onWarn}:{}};let s=Xe(this.persistence).find(i=>i.key===this.key);s&&(this.cache={entries:s.value,timestamp:s.storedAt});}}get(){return this.cache?Date.now()-this.cache.timestamp>this.ttlMs?(this.cache=null,null):this.cache.entries:null}set(e){this.cache={entries:e,timestamp:Date.now()},this.persist();}clear(){this.cache=null,this.persist();}persist(){this.persistence&&Qe(this.cache?[{key:this.key,value:this.cache.entries,storedAt:this.cache.timestamp}]:[],this.persistence);}};var Pt=0;function vn(t){let e=t.docnum??`${Date.now().toString().padStart(13,"0")}${String(Pt++).padStart(3,"0")}`;Pt>=1e3&&(Pt=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 wi(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 Ai(t,e){return t.filter(n=>n.name===e).length}function Tn(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=Ai(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=wi(s.name,o,a);c&&n.push(c);}s.children&&s.children.length>0&&Tn(s.children,e,n);}}function bn(t,e){if(!e)return [];let n=new Map;for(let s of e.segments)n.set(s.name,s);let r=[];return Tn(t,n,r),r}var _i=new XMLBuilder({ignoreAttributes:false,attributeNamePrefix:"@_",format:true,suppressEmptyNode:false});function wn(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=wn(n),s=e[n.name];s?Array.isArray(s)?s.push(r):e[n.name]=[s,r]:e[n.name]=r;}return e}function An(t,e,n){let r={};for(let i of n){let o=wn(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 _i.build(s)}var Ze=class{constructor(e,n,r){this.httpClient=e;this.sapClient=n;this.partnerDefaults=r;}async send(e){let n={...this.partnerDefaults,...e.partnerOverrides},r=vn({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 d=bn(e.segments,e.metadata);if(d.length>0)return {success:false,docnum:r.DOCNUM,message:"Validation failed",violations:d}}let s=An(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}}};var et=30,Ei=5e3;function Ri(t,e,n,r){let s=t?._meta?.progressToken,i=t?.sendNotification;if(s===void 0||typeof i!="function"||r.afterMs<=0)return ()=>{};let a=setInterval(()=>{let c=Date.now()-n;i({method:"notifications/progress",params:{progressToken:s,progress:Math.round(c/1e3),...r.budgetMs?{total:Math.round(r.budgetMs/1e3)}:{},message:`${e.toolName}: ${Math.round(c/1e3)} s, ${e.sapCalls.count} SAP-Aufruf(e)`}}).catch(()=>{});},r.afterMs);return a.unref?.(),()=>clearInterval(a)}var Pi="SAP_TLS_VERIFY=false: Die Zertifikatspr\xFCfung ist abgeschaltet \u2014 die Verbindung zu SAP ist nicht gegen einen Angreifer in der Leitung gesch\xFCtzt. Richtiger Weg: Root-CA \xFCber SAP_CA_CERT (bzw. NODE_EXTRA_CA_CERTS) einbinden und SAP_TLS_VERIFY wieder entfernen.";function ki(t){return {...t,content:[...t.content,{type:"text",text:`\u26A0\uFE0F ${Pi}`}]}}function Ii(t,e,n,r,s){return {content:[{type:"text",text:JSON.stringify({truncated:true,reason:`Zeitbudget von ${Math.round(s/1e3)} s \xFCberschritten \u2014 der Aufruf wurde abgebrochen, damit die Verbindung nicht h\xE4ngen bleibt.`,hint:"Grenzen Sie die Anfrage ein (top/skip, filter, search) oder erh\xF6hen Sie --tool-timeout. Der Wert sollte unter der kleinsten Client-Grenze bleiben (Cloudflare 100 s, n8n-MCP-Transport 300 s).",tool:t,correlationId:e,elapsedMs:n,sapCalls:r},null,2)}],isError:true}}function Oi(t,e){return {content:[{type:"text",text:JSON.stringify({aborted:true,reason:"Der Client hat die Verbindung verworfen; die Arbeit wurde eingestellt.",tool:t,correlationId:e})}],isError:true}}function Cn(t,e,n={}){let{toolTimeoutMs:r,logger:s,tlsVerifyDisabled:i}=n,o=n.progressAfterMs??Ei;return async(a,c)=>{let l=zt(),{signal:u,deadline:d}=Yt(c?.signal,r),m={correlationId:l,toolName:t,signal:u,deadline:d,sapCalls:{count:0}},p=Date.now();return Kt(m,async()=>{let f=Ri(c,m,p,{afterMs:o,...r?{budgetMs:r}:{}}),g="ok",y,h=(async()=>e(a,c))();h.catch(()=>{});try{y=await Promise.race([h,Ni(u,t)]),y?.isError&&(g="error");}catch(v){if(v instanceof D)g=v.timedOut?"timeout":"aborted",y=v.timedOut?Ii(t,l,Date.now()-p,m.sapCalls.count,r??0):Oi(t,l);else {g="error";let k=v instanceof Error?v.message:String(v);y={content:[{type:"text",text:JSON.stringify({error:k,tool:t,correlationId:l})}],isError:true};}}g==="ok"&&u?.aborted&&(g=u.reason?.name==="TimeoutError"?"timeout":"aborted"),f();let S=i?ki(y):y;return s?.info({correlationId:l,tool:t,outcome:g,durationMs:Date.now()-p,sapCalls:m.sapCalls.count,resultBytes:Di(y),...r?{budgetMs:r}:{}},"MCP tool call"),S})}}function Ni(t,e){return t?new Promise((n,r)=>{let s=()=>{let i=t.reason?.name==="TimeoutError";r(new D(i,e));};if(t.aborted){s();return}t.addEventListener("abort",s,{once:true});}):new Promise(()=>{})}function Di(t){if(!t?.content)return 0;let e=0;for(let n of t.content)typeof n?.text=="string"&&(e+=Buffer.byteLength(n.text,"utf-8"));return e}var xi=1440*60*1e3,Mi="metadata-cache.json",E=class{cache=new Map;ttlMs;baseUrl;persistence;constructor(e={}){if(this.ttlMs=e.ttlMs??xi,this.baseUrl=Ui(e.baseUrl),e.persistDir){this.persistence={directory:e.persistDir,fileName:Mi,ttlMs:this.ttlMs,...e.onWarn?{onWarn:e.onWarn}:{}};for(let n of Xe(this.persistence))this.cache.set(n.key,{value:n.value,storedAt:n.storedAt});}}get size(){return this.cache.size}get(e){let n=this.key(e),r=this.cache.get(n);if(r){if(Date.now()-r.storedAt>this.ttlMs){this.cache.delete(n);return}return r.value}}set(e,n){this.cache.set(this.key(e),{value:n,storedAt:Date.now()}),this.persist();}has(e){return this.get(e)!==void 0}clear(){this.cache.clear(),this.persist();}key(e){let n=e.replace(/\/+$/,"");return this.baseUrl?`${this.baseUrl}|${n}`:n}persist(){this.persistence&&Qe([...this.cache.entries()].map(([e,n])=>({key:e,value:n.value,storedAt:n.storedAt})),this.persistence);}};function Ui(t){return t?t.replace(/\/+$/,"").toLowerCase():""}var _n="0.0.0-unknown",En="guniweb-sap-mcp";function Hi(t){if(typeof t!="string"||t.length===0)return _n;let e=resolve(t);for(;;){try{let r=readFileSync(join(e,"package.json"),"utf-8"),s=JSON.parse(r);if(s.name===En&&typeof s.version=="string")return s.version}catch{}let n=dirname(e);if(n===e)return _n;e=n;}}var Rn=Hi(import.meta.dirname),Pn=En;function kn(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$1.string().describe("OData service URL, e.g., /sap/opu/odata/sap/API_BUSINESS_PARTNER"),entitySet:z$1.string().describe("Entity set to query, e.g., A_BusinessPartner"),question:z$1.string().describe('Natural language question, e.g., "show me all customers in Germany"'),allowWrite:z$1.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?"":`
52
+ `)}`)}return o.current=a.value,o.currentRaw=a.raw,o}get version(){return this.currentVersion}get(e){return this.current.destinations.get(e)}names(){return [...this.current.destinations.keys()]}defaultDestination(){let e=this.current.destinations.get(Y);if(e)return e;if(this.current.destinations.size===1)return this.current.destinations.values().next().value}tokens(){return this.current.tokens}onChange(e){return this.changeListeners.add(e),()=>this.changeListeners.delete(e)}onError(e){return this.errorListeners.add(e),()=>this.errorListeners.delete(e)}reload(){return this.source==="env"?Promise.resolve({ok:true,changed:false}):this.reloadInFlight?(this.reloadQueued=true,this.reloadInFlight):(this.reloadInFlight=this.doReload().finally(()=>{this.reloadInFlight=void 0,this.reloadQueued&&(this.reloadQueued=false,this.reload());}),this.reloadInFlight)}watch(){if(!(this.source==="env"||!this.filePath)&&(this.sighupHandler||(this.sighupHandler=()=>{this.reload();},process.on("SIGHUP",this.sighupHandler)),this.observeFile&&!this.polling&&(this.polling=true,watchFile(this.filePath,{persistent:false,interval:mn},(e,n)=>{(e.mtimeMs!==n.mtimeMs||e.size!==n.size)&&this.scheduleReload();}),this.startupRecheck=setTimeout(()=>{this.startupRecheck=void 0,this.scheduleReload();},mn+500),this.startupRecheck.unref()),this.observeFile&&!this.watcher))try{this.watcher=watch(dirname(this.filePath),{persistent:!1},()=>{this.scheduleReload();}),this.watcher.on("error",e=>{this.warn(`Dateibeobachtung f\xFCr ${this.filePath} ausgefallen (${e.message}); SIGHUP l\xE4dt weiterhin neu.`),this.watcher=void 0;});}catch(e){this.warn(`Dateibeobachtung f\xFCr ${this.filePath} nicht m\xF6glich (${e instanceof Error?e.message:String(e)}); SIGHUP l\xE4dt weiterhin neu.`);}}close(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=void 0),this.startupRecheck&&(clearTimeout(this.startupRecheck),this.startupRecheck=void 0),this.watcher&&(this.watcher.close(),this.watcher=void 0),this.polling&&this.filePath&&(unwatchFile(this.filePath),this.polling=false),this.sighupHandler&&(process.off("SIGHUP",this.sighupHandler),this.sighupHandler=void 0),this.changeListeners.clear(),this.errorListeners.clear();}scheduleReload(){this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.debounceTimer=void 0,this.reload();},oi),this.debounceTimer.unref();}async doReload(){let e=await this.readAndParse();if(!e.ok){for(let r of this.errorListeners)r(e.errors);return {ok:false,errors:e.errors}}if(e.raw===this.currentRaw)return {ok:true,changed:false};this.current=e.value,this.currentRaw=e.raw,this.currentVersion+=1;let n={version:this.currentVersion,names:this.names(),tokenCount:this.current.tokens.length};for(let r of this.changeListeners)r(n);return {ok:true,changed:true}}async readAndParse(){if(!this.filePath)return {ok:false,errors:["Kein Dateipfad konfiguriert."]};let e;try{e=await readFile(this.filePath,"utf8");}catch(s){let i=s.code,o=i==="ENOENT"?"Datei nicht gefunden":s.message;return {ok:false,errors:[`${this.filePath}: ${o}`],missing:i==="ENOENT"}}let n;try{n=JSON.parse(e);}catch(s){return {ok:false,errors:[`${this.filePath}: kein g\xFCltiges JSON (${s instanceof Error?s.message:String(s)})`]}}let r=Ve(n,this.env);return r.ok?(this.envDefault&&(r.value.destinations.has(Y)?this.warnedFileWins||(this.warnedFileWins=true,this.warn(`Sowohl ${this.filePath} als auch die SAP_*-Umgebung definieren die Destination "default" \u2014 die Datei gilt, die Umgebung wird ignoriert.`)):r.value.destinations.set(Y,this.envDefault)),{ok:true,value:r.value,raw:e}):r}};var de=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 ci=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",parseTagValue:false,processEntities:false,htmlEntities:false,isArray:t=>t==="IDOC"||t.startsWith("E1")||t.startsWith("E2")}),Ae=new Set(["@_SEGMENT","@_BEGIN"]);function Rt(t,e){return !!(t.startsWith("E1")||t.startsWith("E2")||typeof e=="object"&&e!==null&&"@_SEGMENT"in e)}function fn(t){let e=[];for(let[n,r]of Object.entries(t)){if(Ae.has(n)||!Rt(n,r))continue;let s=Array.isArray(r)?r:[r];for(let i of s){if(typeof i!="object"||i===null)continue;let o={},a=[];for(let[c,l]of Object.entries(i))if(!Ae.has(c))if(Rt(c,l)){let u=Array.isArray(l)?l:[l];for(let d of u){if(typeof d!="object"||d===null)continue;let m={},p=fn(d);for(let[f,g]of Object.entries(d))Ae.has(f)||!Rt(f,g)&&typeof g!="object"&&(m[f]=String(g));a.push({name:c,fields:m,...p.length>0?{children:p}:{}});}}else typeof l!="object"&&(o[c]=String(l));e.push({name:n,fields:o,...a.length>0?{children:a}:{}});}}return e}function gn(t){let e=ci.parse(t),n=Object.keys(e).find(d=>d!=="?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 d=Array.isArray(a)?a[0]:a;for(let[m,p]of Object.entries(d))!Ae.has(m)&&typeof p!="object"&&(c[m]=String(p));}let l={};for(let[d,m]of Object.entries(o))d==="EDI_DC40"||Ae.has(d)||(l[d]=m);let u=fn(l);return {idocType:r,controlRecord:c,segments:u}}var li=10*1024*1024,Ye=class extends Error{};function di(t,e){return new Promise((n,r)=>{let s=[],i=0,o=false;t.on("data",a=>{if(!o){if(i+=a.length,i>e){o=true,s.length=0,r(new Ye(`Request body exceeds ${e} bytes`));return}s.push(a);}}),t.on("end",()=>{o||n(Buffer.concat(s).toString("utf-8"));}),t.on("error",a=>{o||r(a);});})}function ui(t,e){if(e.length===0||typeof t!="string")return false;if(t.startsWith("Bearer "))return Te(t.slice(7),e);if(t.startsWith("Basic ")){let n=Buffer.from(t.slice(6),"base64").toString("utf-8"),r=n.indexOf(":");return r===-1?false:Te(n.slice(r+1),e)}return false}function pi(t){if(typeof t!="string")return true;let e=(t.split(";")[0]??"").trim().toLowerCase();return e.endsWith("xml")||e==="application/x-sap.idoc"}function Je(t,e={}){let n=e.maxBodyBytes??li;return (r,s)=>{if(r.method!=="POST"){s.writeHead(405,{"Content-Type":"text/plain"}),s.end("Method Not Allowed");return}if(e.secret!==void 0&&!ui(r.headers.authorization,e.secret)){s.writeHead(401,{"Content-Type":"text/plain","WWW-Authenticate":'Basic realm="idoc-webhook"'}),s.end("Unauthorized"),r.destroy();return}if(!pi(r.headers["content-type"])){s.writeHead(415,{"Content-Type":"text/plain"}),s.end("Unsupported Media Type: expected XML"),r.destroy();return}di(r,n).then(i=>{let{idocType:o,controlRecord:a,segments:c}=gn(i),l={docnum:a.DOCNUM||`UNKNOWN_${Date.now()}`,idocType:o,mesType:a.MESTYP||"",receivedAt:new Date,rawXml:i,parsed:{controlRecord:a,segments:c}};t.add(l),s.writeHead(200,{"Content-Type":"text/plain"}),s.end("OK");}).catch(i=>{if(i instanceof Ye){s.writeHead(413,{"Content-Type":"text/plain"}),s.end("Payload Too Large"),r.destroy();return}s.writeHead(400,{"Content-Type":"text/plain"}),s.end(`Parse error: ${i instanceof Error?i.message:String(i)}`);});}}function hn(t,e,n={}){let r=Je(e,n);return new Promise((s,i)=>{let o=createServer((c,l)=>{c.method==="POST"&&c.url==="/idoc"?r(c,l):(l.writeHead(404,{"Content-Type":"text/plain"}),l.end("Not Found"));}),a=()=>{let c=o.address(),l=typeof c=="object"&&c!==null?c.port:t;s({port:l,close:()=>new Promise((u,d)=>{o.close(m=>m?d(m):u());})});};n.host?o.listen(t,n.host,a):o.listen(t,a),o.on("error",i);})}var Sn=1;function Xe(t){if(!t.directory)return [];let e=join(t.directory,t.fileName),n;try{n=readFileSync(e,"utf-8");}catch{return []}try{let r=JSON.parse(n);if(r?.version!==Sn||!Array.isArray(r.entries))return t.onWarn?.(`Cache-Datei ${e} hat ein unbekanntes Format und wird verworfen.`),[];let s=Date.now()-t.ttlMs;return r.entries.filter(i=>i&&typeof i.key=="string"&&typeof i.storedAt=="number"&&i.storedAt>s)}catch(r){return t.onWarn?.(`Cache-Datei ${e} ist unlesbar und wird verworfen. Der Cache wird aus SAP neu aufgebaut.`,r),[]}}function Qe(t,e){if(!e.directory)return;let n=join(e.directory,e.fileName),r=`${n}.tmp`;try{mkdirSync(dirname(n),{recursive:!0}),writeFileSync(r,JSON.stringify({version:Sn,entries:t}),"utf-8"),renameSync(r,n);}catch(s){e.onWarn?.(`Cache konnte nicht nach ${n} geschrieben werden. Der Betrieb l\xE4uft weiter, nach einem Neustart ist der Cache aber leer.`,s);try{unlinkSync(r);}catch{}}}var bi=30,Ti="catalog-cache.json",vn="catalog",H=class{cache=null;ttlMs;key;persistence;constructor(e={}){let n=typeof e=="number"?{ttlMinutes:e}:e;this.ttlMs=(n.ttlMinutes??bi)*60*1e3;let r=(n.baseUrl??"").replace(/\/+$/,"").toLowerCase();if(this.key=r?`${r}|${vn}`:vn,n.persistDir){this.persistence={directory:n.persistDir,fileName:Ti,ttlMs:this.ttlMs,...n.onWarn?{onWarn:n.onWarn}:{}};let s=Xe(this.persistence).find(i=>i.key===this.key);s&&(this.cache={entries:s.value,timestamp:s.storedAt});}}get(){return this.cache?Date.now()-this.cache.timestamp>this.ttlMs?(this.cache=null,null):this.cache.entries:null}set(e){this.cache={entries:e,timestamp:Date.now()},this.persist();}clear(){this.cache=null,this.persist();}persist(){this.persistence&&Qe(this.cache?[{key:this.key,value:this.cache.entries,storedAt:this.cache.timestamp}]:[],this.persistence);}};var Pt=0;function bn(t){let e=t.docnum??`${Date.now().toString().padStart(13,"0")}${String(Pt++).padStart(3,"0")}`;Pt>=1e3&&(Pt=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 wi(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 Ai(t,e){return t.filter(n=>n.name===e).length}function Tn(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=Ai(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=wi(s.name,o,a);c&&n.push(c);}s.children&&s.children.length>0&&Tn(s.children,e,n);}}function wn(t,e){if(!e)return [];let n=new Map;for(let s of e.segments)n.set(s.name,s);let r=[];return Tn(t,n,r),r}var _i=new XMLBuilder({ignoreAttributes:false,attributeNamePrefix:"@_",format:true,suppressEmptyNode:false});function An(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=An(n),s=e[n.name];s?Array.isArray(s)?s.push(r):e[n.name]=[s,r]:e[n.name]=r;}return e}function Cn(t,e,n){let r={};for(let i of n){let o=An(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 _i.build(s)}var Ze=class{constructor(e,n,r){this.httpClient=e;this.sapClient=n;this.partnerDefaults=r;}async send(e){let n={...this.partnerDefaults,...e.partnerOverrides},r=bn({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 d=wn(e.segments,e.metadata);if(d.length>0)return {success:false,docnum:r.DOCNUM,message:"Validation failed",violations:d}}let s=Cn(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}}};var et=30,Ei=5e3;function Ri(t,e,n,r){let s=t?._meta?.progressToken,i=t?.sendNotification;if(s===void 0||typeof i!="function"||r.afterMs<=0)return ()=>{};let a=setInterval(()=>{let c=Date.now()-n;i({method:"notifications/progress",params:{progressToken:s,progress:Math.round(c/1e3),...r.budgetMs?{total:Math.round(r.budgetMs/1e3)}:{},message:`${e.toolName}: ${Math.round(c/1e3)} s, ${e.sapCalls.count} SAP-Aufruf(e)`}}).catch(()=>{});},r.afterMs);return a.unref?.(),()=>clearInterval(a)}var Pi="SAP_TLS_VERIFY=false: Die Zertifikatspr\xFCfung ist abgeschaltet \u2014 die Verbindung zu SAP ist nicht gegen einen Angreifer in der Leitung gesch\xFCtzt. Richtiger Weg: Root-CA \xFCber SAP_CA_CERT (bzw. NODE_EXTRA_CA_CERTS) einbinden und SAP_TLS_VERIFY wieder entfernen.";function ki(t){return {...t,content:[...t.content,{type:"text",text:`\u26A0\uFE0F ${Pi}`}]}}function Ii(t,e,n,r,s){return {content:[{type:"text",text:JSON.stringify({truncated:true,reason:`Zeitbudget von ${Math.round(s/1e3)} s \xFCberschritten \u2014 der Aufruf wurde abgebrochen, damit die Verbindung nicht h\xE4ngen bleibt.`,hint:"Grenzen Sie die Anfrage ein (top/skip, filter, search) oder erh\xF6hen Sie --tool-timeout. Der Wert sollte unter der kleinsten Client-Grenze bleiben (Cloudflare 100 s, n8n-MCP-Transport 300 s).",tool:t,correlationId:e,elapsedMs:n,sapCalls:r},null,2)}],isError:true}}function Oi(t,e){return {content:[{type:"text",text:JSON.stringify({aborted:true,reason:"Der Client hat die Verbindung verworfen; die Arbeit wurde eingestellt.",tool:t,correlationId:e})}],isError:true}}function _n(t,e,n={}){let{toolTimeoutMs:r,logger:s,tlsVerifyDisabled:i}=n,o=n.progressAfterMs??Ei;return async(a,c)=>{let l=zt(),{signal:u,deadline:d}=Yt(c?.signal,r),m={correlationId:l,toolName:t,signal:u,deadline:d,sapCalls:{count:0}},p=Date.now();return Kt(m,async()=>{let f=Ri(c,m,p,{afterMs:o,...r?{budgetMs:r}:{}}),g="ok",y,h=(async()=>e(a,c))();h.catch(()=>{});try{y=await Promise.race([h,Ni(u,t)]),y?.isError&&(g="error");}catch(v){if(v instanceof D)g=v.timedOut?"timeout":"aborted",y=v.timedOut?Ii(t,l,Date.now()-p,m.sapCalls.count,r??0):Oi(t,l);else {g="error";let k=v instanceof Error?v.message:String(v);y={content:[{type:"text",text:JSON.stringify({error:k,tool:t,correlationId:l})}],isError:true};}}g==="ok"&&u?.aborted&&(g=u.reason?.name==="TimeoutError"?"timeout":"aborted"),f();let S=i?ki(y):y;return s?.info({correlationId:l,tool:t,outcome:g,durationMs:Date.now()-p,sapCalls:m.sapCalls.count,resultBytes:Di(y),...r?{budgetMs:r}:{}},"MCP tool call"),S})}}function Ni(t,e){return t?new Promise((n,r)=>{let s=()=>{let i=t.reason?.name==="TimeoutError";r(new D(i,e));};if(t.aborted){s();return}t.addEventListener("abort",s,{once:true});}):new Promise(()=>{})}function Di(t){if(!t?.content)return 0;let e=0;for(let n of t.content)typeof n?.text=="string"&&(e+=Buffer.byteLength(n.text,"utf-8"));return e}var xi=1440*60*1e3,Mi="metadata-cache.json",E=class{cache=new Map;ttlMs;baseUrl;persistence;constructor(e={}){if(this.ttlMs=e.ttlMs??xi,this.baseUrl=Ui(e.baseUrl),e.persistDir){this.persistence={directory:e.persistDir,fileName:Mi,ttlMs:this.ttlMs,...e.onWarn?{onWarn:e.onWarn}:{}};for(let n of Xe(this.persistence))this.cache.set(n.key,{value:n.value,storedAt:n.storedAt});}}get size(){return this.cache.size}get(e){let n=this.key(e),r=this.cache.get(n);if(r){if(Date.now()-r.storedAt>this.ttlMs){this.cache.delete(n);return}return r.value}}set(e,n){this.cache.set(this.key(e),{value:n,storedAt:Date.now()}),this.persist();}has(e){return this.get(e)!==void 0}clear(){this.cache.clear(),this.persist();}key(e){let n=e.replace(/\/+$/,"");return this.baseUrl?`${this.baseUrl}|${n}`:n}persist(){this.persistence&&Qe([...this.cache.entries()].map(([e,n])=>({key:e,value:n.value,storedAt:n.storedAt})),this.persistence);}};function Ui(t){return t?t.replace(/\/+$/,"").toLowerCase():""}var En="0.0.0-unknown",Rn="guniweb-sap-mcp";function Hi(t){if(typeof t!="string"||t.length===0)return En;let e=resolve(t);for(;;){try{let r=readFileSync(join(e,"package.json"),"utf-8"),s=JSON.parse(r);if(s.name===Rn&&typeof s.version=="string")return s.version}catch{}let n=dirname(e);if(n===e)return En;e=n;}}var Pn=Hi(import.meta.dirname),kn=Rn;function In(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$1.string().describe("OData service URL, e.g., /sap/opu/odata/sap/API_BUSINESS_PARTNER"),entitySet:z$1.string().describe("Entity set to query, e.g., A_BusinessPartner"),question:z$1.string().describe('Natural language question, e.g., "show me all customers in Germany"'),allowWrite:z$1.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?"":`
53
53
 
54
54
  IMPORTANT \u2014 READ-ONLY MODE:
55
55
  You MUST only generate read queries (sap_query or sap_nl_query). Do NOT use sap_create, sap_update, or sap_delete.
@@ -110,7 +110,7 @@ Use the \`sap_nl_query\` tool with these parameters:
110
110
  - "orders over 1000 EUR" \u2192 \`{ field: "TotalAmount", operator: "gt", value: 1000 }\`
111
111
  - "products containing 'bolt'" \u2192 \`{ field: "ProductName", operator: "contains", value: "bolt" }\`
112
112
  - "invoices since January 2024" \u2192 \`{ field: "InvoiceDate", operator: "ge", value: "2024-01-01" }\`
113
- ${i}`}}]}});}function In(t){t.registerPrompt("explore-sales-orders",{title:"Explore Sales Orders",description:"Discover and query SAP Sales Order data step by step",argsSchema:{serviceUrl:z$1.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:
113
+ ${i}`}}]}});}function On(t){t.registerPrompt("explore-sales-orders",{title:"Explore Sales Orders",description:"Discover and query SAP Sales Order data step by step",argsSchema:{serviceUrl:z$1.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:
114
114
 
115
115
  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"}
116
116
  2. Use sap_list_services to discover available entity sets
@@ -140,18 +140,18 @@ Use sap_query with appropriate filters to find the material data.`}}]})),t.regis
140
140
  3. Use sap_get_metadata to understand the Business Partner data model
141
141
  ${s}`}}]}});}var rt=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 Bi(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(`
142
142
 
143
- `),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 On(t,e){let n=[],r=t.split(`--${e}`);for(let s of r){let i=s.trim();if(!i||i==="--")continue;let o=i.match(/Content-Type:\s*multipart\/mixed;\s*boundary=([^\s\r\n]+)/i);if(o){let c=o[1],l=i.indexOf(`
143
+ `),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 Nn(t,e){let n=[],r=t.split(`--${e}`);for(let s of r){let i=s.trim();if(!i||i==="--")continue;let o=i.match(/Content-Type:\s*multipart\/mixed;\s*boundary=([^\s\r\n]+)/i);if(o){let c=o[1],l=i.indexOf(`
144
144
 
145
- `);if(l!==-1){let u=i.substring(l+2),d=On(u,c);n.push(...d);}continue}let a=Bi(i);a&&n.push(a);}return n}function Nn(t,e){let n=e.match(/boundary=([^\s;]+)/);if(!n)return [];let r=n[1],s=t.replace(/\r\n/g,`
146
- `);return On(s,r)}var Vi=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function Dn(t){return typeof t=="number"?String(t):typeof t=="string"&&Vi.test(t)?`guid'${t}'`:`'${String(t)}'`}function ue(t,e){let n=Object.entries(e);if(n.length===1){let s=n[0];return `${t}(${Dn(s[1])})`}let r=n.map(([s,i])=>`${s}=${Dn(i)}`);return `${t}(${r.join(",")})`}function kt(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 qi(t){return t.key?ue(t.entitySet,t.key):t.entitySet}function Mn(t){let e=qi(t),n=t.method.toUpperCase(),r=n==="PATCH",s=r?"POST":n,i=[];if(i.push("Content-Type: application/http"),i.push("Content-Transfer-Encoding: binary"),i.push(""),i.push(`${s} ${e} HTTP/1.1`),n==="GET"?i.push("Accept: application/json"):(i.push("Content-Type: application/json"),i.push("Accept: application/json"),r&&i.push("X-HTTP-Method: MERGE"),t.data&&"__etag"in t.data&&i.push(`If-Match: ${t.data.__etag}`)),i.push(""),t.data&&(n==="POST"||n==="PATCH"||n==="DELETE")){let{__etag:o,...a}=t.data;Object.keys(a).length>0&&i.push(JSON.stringify(a));}return i.join(`\r
147
- `)}function Un(t,e,n){let r=`batch_${randomUUID()}`,s=[];for(let a of e)s.push(`--${r}\r
148
- ${Mn(a)}`);if(n)for(let a of n){let c=`changeset_${randomUUID()}`,l=[];for(let d of a.operations)l.push(`--${c}\r
149
- ${Mn(d)}`);l.push(`--${c}--`);let u=l.join(`\r
145
+ `);if(l!==-1){let u=i.substring(l+2),d=Nn(u,c);n.push(...d);}continue}let a=Bi(i);a&&n.push(a);}return n}function Dn(t,e){let n=e.match(/boundary=([^\s;]+)/);if(!n)return [];let r=n[1],s=t.replace(/\r\n/g,`
146
+ `);return Nn(s,r)}var Vi=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function xn(t){return typeof t=="number"?String(t):typeof t=="string"&&Vi.test(t)?`guid'${t}'`:`'${String(t)}'`}function ue(t,e){let n=Object.entries(e);if(n.length===1){let s=n[0];return `${t}(${xn(s[1])})`}let r=n.map(([s,i])=>`${s}=${xn(i)}`);return `${t}(${r.join(",")})`}function kt(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 qi(t){return t.key?ue(t.entitySet,t.key):t.entitySet}function Un(t){let e=qi(t),n=t.method.toUpperCase(),r=n==="PATCH",s=r?"POST":n,i=[];if(i.push("Content-Type: application/http"),i.push("Content-Transfer-Encoding: binary"),i.push(""),i.push(`${s} ${e} HTTP/1.1`),n==="GET"?i.push("Accept: application/json"):(i.push("Content-Type: application/json"),i.push("Accept: application/json"),r&&i.push("X-HTTP-Method: MERGE"),t.data&&"__etag"in t.data&&i.push(`If-Match: ${t.data.__etag}`)),i.push(""),t.data&&(n==="POST"||n==="PATCH"||n==="DELETE")){let{__etag:o,...a}=t.data;Object.keys(a).length>0&&i.push(JSON.stringify(a));}return i.join(`\r
147
+ `)}function $n(t,e,n){let r=`batch_${randomUUID()}`,s=[];for(let a of e)s.push(`--${r}\r
148
+ ${Un(a)}`);if(n)for(let a of n){let c=`changeset_${randomUUID()}`,l=[];for(let d of a.operations)l.push(`--${c}\r
149
+ ${Un(d)}`);l.push(`--${c}--`);let u=l.join(`\r
150
150
  `);s.push(`--${r}\r
151
151
  Content-Type: multipart/mixed; boundary=${c}\r
152
152
  \r
153
153
  ${u}`);}s.push(`--${r}--`);let i=s.join(`\r
154
- `),o=`multipart/mixed; boundary=${r}`;return {body:i,contentType:o}}var zi=new Set(["EntityType","Property","NavigationProperty","Association","EntitySet","AssociationSet","PropertyRef","End","FunctionImport","Parameter"]),Ki=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",removeNSPrefix:true,processEntities:false,htmlEntities:false,isArray:t=>zi.has(t)});function It(t){return t===void 0?true:String(t).toLowerCase()!=="false"}function st(t,e){let r=Ki.parse(t).Edmx.DataServices.Schema,s=Array.isArray(r)?r[0]:r,i=s.EntityType??[],o=s.Association??[],a=s.EntityContainer,c=a?.EntitySet??[],l=new Map;for(let g of o)l.set(g["@_Name"],g);let u=i.map(g=>{let y=Gi(g.Property??[]),h=(g.Key?.PropertyRef??[]).map(k=>k["@_Name"]),S=y.filter(k=>h.includes(k.name)),v=Yi(g.NavigationProperty??[],l);return {name:g["@_Name"],keys:S,properties:y,navigationProperties:v}}),d=c.map(g=>({name:g["@_Name"],entityTypeName:Ot(g["@_EntityType"]),creatable:It(g["@_creatable"]),updatable:It(g["@_updatable"]),deletable:It(g["@_deletable"])})),m=a?.FunctionImport??[],p=Ji(Array.isArray(m)?m:[m]),f={version:"v2",serviceUrl:e,entityTypes:u,entitySets:d};return p.length>0&&(f.functionImports=p),f}function Gi(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 Yi(t,e){return t.map(n=>{let r=Ot(n["@_Relationship"]),s=e.get(r),i="Unknown",o=false;if(s){let a=s.End.find(c=>c["@_Role"]===n["@_ToRole"]);a&&(i=Ot(a["@_Type"]),o=a["@_Multiplicity"]==="*");}return {name:n["@_Name"],targetEntityType:i,isCollection:o}})}function Ji(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 Ot(t){let e=t.lastIndexOf(".");return e>=0?t.substring(e+1):t}var Xi=/^\/Date\((-?\d+)([+-]\d{4})?\)\/$/;function Ce(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 $n(t){let e=Xi.exec(t);if(!e)return t;let n=Number(e[1]);return new Date(n).toISOString()}function Y(t){if(t==null)return t;if(Array.isArray(t))return t.map(e=>Y(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]=$n(s):n[r]=Y(s)));return n}return typeof t=="string"?$n(t):t}var Ln=10,_e=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;}async read(e,n,r){let s=kt(r??{});if(n){let i=`${this.serviceUrl}/${ue(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=Ce(o.data);return {...a,results:a.results.map(c=>Y(c))}}return this.fetchWithPagination(`${this.serviceUrl}/${e}`,s,r?.maxPages??Ln)}async query(e,n){let r=kt(n,true),s=n.maxPages??Ln;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=Ce(r.data);return s.results.length>0?Y(s.results[0]):{}}async update(e,n,r){let s=`${this.serviceUrl}/${ue(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}/${ue(e,n)}`,s=this.etagCache.get(e,n)??"*";await this.httpClient.request({method:"delete",url:r,headers:{Accept:"application/json","If-Match":s}}),this.etagCache.update(e,n,void 0);}async functionImport(e,n,r){let s=(r??"POST").toLowerCase(),i={$format:"json"};if(n)for(let[l,u]of Object.entries(n))typeof u=="string"?i[l]=`'${u}'`:i[l]=String(u);let o=await this.httpClient.request({method:s,url:`${this.serviceUrl}/${e}`,headers:{Accept:"application/json"},params:i}),a=Ce(o.data);if(a.results.length>0)return Y(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}=Un(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=Nn(i.data,o),c=[...e,...(n??[]).flatMap(l=>l.operations)];for(let l=0;l<a.length&&l<c.length;l++)a[l].entitySet=c[l].entitySet;return {results:a}}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml"}});return st(e.data,this.serviceUrl)}async fetchWithPagination(e,n,r){let s=[],i,o=e,a=n,c=0,l=false;for(;o&&c<r;){if(c>0&&$e()){l=true;break}let m=(await this.httpClient.request({method:"get",url:o,headers:{Accept:"application/json"},params:a})).data,p=Ce(m),f=p.results.map(y=>Y(y));s.push(...f),c===0&&p.count!==void 0&&(i=p.count),c++;let g=m?.d;g&&typeof g.__next=="string"?(o=g.__next,a=void 0):o=void 0;}let u={results:s};return i!==void 0&&(u.count=i),o&&(u.nextLink=o),l&&(u.truncated=true,u.truncationReason=`Nach ${c} Seite(n) abgebrochen: Zeitbudget des Tool-Aufrufs ersch\xF6pft oder Client hat die Verbindung verworfen. Mit skip=${s.length} weiterlesen.`),u}};function Fn(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 jn(t){return typeof t=="string"?`'${t}'`:String(t)}function B(t,e){let n=Object.entries(e);if(n.length===1){let[,s]=n[0];return `${t}(${jn(s)})`}let r=n.map(([s,i])=>`${s}=${jn(i)}`).join(",");return `${t}(${r})`}function it(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 Hn(t,e){let n=[];for(let s=0;s<e.length;s++){let i=e[s],o=i.key?B(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 Bn(t){let e=t.split(".");return e[e.length-1]}function Qi(t,e){return {name:t,type:e.$Type??"Edm.String",nullable:e.$Nullable??false,maxLength:e.$MaxLength,precision:e.$Precision,scale:e.$Scale}}function Vn(t,e){let n=JSON.parse(t),r=[],s=[],i=[];for(let[a,c]of Object.entries(n)){if(a.startsWith("$"))continue;let l=c;for(let[u,d]of Object.entries(l)){let m=d;if(m.$Kind==="Action"||m.$Kind==="Function"){let p=m.$IsBound??false,f=m.$Parameter??[],g=p&&f.length>0?f[0].$Name:void 0,y=(p?f.slice(1):f).map(S=>({name:S.$Name,type:S.$Type??"Edm.String",...S.$Nullable===true?{nullable:true}:{}})),h=m.$ReturnType?m.$ReturnType.$Type:void 0;i.push({name:u,isBound:p,...g?{bindingParameter:g}:{},parameters:y,...h?{returnType:h}:{}});}else if(m.$Kind==="EntityType"){let p=[],f=[];for(let[h,S]of Object.entries(m)){if(h.startsWith("$"))continue;let v=S;v.$Kind==="NavigationProperty"?f.push({name:h,targetEntityType:Bn(v.$Type),isCollection:v.$Collection??false}):p.push(Qi(h,v));}let y=(m.$Key??[]).map(h=>{let S=p.find(v=>v.name===h);return S||{name:h,type:"Edm.String",nullable:false}});r.push({name:u,keys:y,properties:p,navigationProperties:f});}else if(m.$Kind==="EntityContainer")for(let[p,f]of Object.entries(m)){if(p.startsWith("$"))continue;let g=f;g.$Collection&&g.$Type&&s.push({name:p,entityTypeName:Bn(g.$Type),creatable:Nt(g,"InsertRestrictions","Insertable"),updatable:Nt(g,"UpdateRestrictions","Updatable"),deletable:Nt(g,"DeleteRestrictions","Deletable")});}}}let o={version:"v4",serviceUrl:e,entityTypes:r,entitySets:s};return i.length>0&&(o.actions=i),o}function Nt(t,e,n){for(let[r,s]of Object.entries(t)){if(!r.startsWith("@")||!r.endsWith(e))continue;let i=s?.[n];if(typeof i=="boolean")return i}return true}var Zi=10,ot=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;}async read(e,n,r){if(n){let l=`${this.serviceUrl}/${B(e,n)}`,u=r?it(r):void 0,d=await this.httpClient.request({method:"get",url:l,params:u}),m=d.data,p=d.headers.etag??d.headers.ETag;return p&&this.etagCache.set(e,n,p),{results:[m]}}let s=r?it(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??Zi,s=it(n,true),o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=[...o.value??[]],c=o["@odata.count"],l=o["@odata.nextLink"],u=1,d=false;for(;l&&u<r;){if($e()){d=true;break}let p=this.resolveNextLink(l),g=(await this.httpClient.request({method:"get",url:p})).data,y=g.value??[];a.push(...y),l=g["@odata.nextLink"],u++;}let m={results:a,count:c};return l&&(m.nextLink=l),d&&(m.truncated=true,m.truncationReason=`Nach ${u} Seite(n) abgebrochen: Zeitbudget des Tool-Aufrufs ersch\xF6pft oder Client hat die Verbindung verworfen. Mit skip=${a.length} weiterlesen.`),m}async create(e,n){return (await this.httpClient.request({method:"post",url:`${this.serviceUrl}/${e}`,data:n,headers:{"Content-Type":"application/json"}})).data}async update(e,n,r){let s=`${this.serviceUrl}/${B(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}/${B(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}=Hn(this.serviceUrl,e),s=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:n,headers:{"Content-Type":r,Accept:"application/json"}}),i=Fn(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}/${B(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}/${B(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 Vn(n,this.serviceUrl)}resolveNextLink(e){return e.startsWith("http://")||e.startsWith("https://")?e:`${this.serviceUrl}/${e}`}};function Dt(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 O=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.metadataCache=r??new E;}protocolClient=null;version=null;initPromise=null;etagCache=new rt;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=Dt(n,this.serviceUrl),r=!0;}catch{this.version=Dt("",this.serviceUrl);}if(this.protocolClient=this.createProtocolClient(this.version),r&&this.version==="v2"&&n)try{let s=st(n,this.serviceUrl);this.metadataCache.set(this.serviceUrl,s);}catch{}}createProtocolClient(e){return e==="v4"?new ot(this.httpClient,this.serviceUrl,this.etagCache):new _e(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 _e)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 qn(t,e,n){let r=n??new E,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),u=await new O(e,a,r).getMetadata(),d=u.entitySets.find(y=>y.name===c);if(!d)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity set '${c}' not found in service ${a}`,availableEntitySets:u.entitySets.map(y=>y.name)},null,2)}]};let m=u.entityTypes.find(y=>y.name===d.entityTypeName);if(!m)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity type '${d.entityTypeName}' not found in metadata`},null,2)}]};let p=new Set(m.keys.map(y=>y.name)),f=m.properties.map(y=>({name:y.name,type:y.type,isKey:p.has(y.name)})),g=to(m.properties,u.version);return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({entitySet:c,version:u.version,properties:f,sampleFilter:g},null,2)}]}});}function to(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 Wn(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 zn(t,e,n){let r=n??new E,s=new ResourceTemplate("sap://metadata/{serviceUrl}",{list:void 0});t.registerResource("sap-metadata",s,{title:"SAP Service Metadata",description:"Entity types, properties, keys, and navigation properties for an SAP OData service",mimeType:"application/json"},async(i,o)=>{let a=decodeURIComponent(o.serviceUrl),l=await new O(e,a,r).getMetadata();return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({version:l.version,serviceUrl:l.serviceUrl,entityTypes:l.entityTypes,entitySets:l.entitySets},null,2)}]}});}var xt={"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 so={$TODAY:()=>Mt(new Date),$YESTERDAY:()=>{let t=new Date;return t.setDate(t.getDate()-1),Mt(t)},$MONTH_START:()=>{let t=new Date;return `${t.getFullYear()}-${Ut(t.getMonth()+1)}-01`},$YEAR_START:()=>`${new Date().getFullYear()}-01-01`,$MONTH_AGO:()=>{let t=new Date;return t.setMonth(t.getMonth()-1),Mt(t)}};function Mt(t){return `${t.getFullYear()}-${Ut(t.getMonth()+1)}-${Ut(t.getDate())}`}function Ut(t){return t.toString().padStart(2,"0")}function io(t){if(typeof t!="string")return t;let e=so[t];return e?e():t}function Kn(t){return {...t,queryTemplates:t.queryTemplates.map(e=>({...e,filters:e.filters.map(n=>({...n,value:io(n.value)}))}))}}function Gn(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=xt[s];if(!i)return {contents:[{uri:n.toString(),mimeType:"application/json",text:JSON.stringify({error:`Unknown domain '${s}'`,availableDomains:Object.keys(xt)},null,2)}]};let o=Kn(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 $t={core:["test-connection","sap_list_services","sap_get_metadata","sap_discover_services","sap_nl_query"],odata:["sap_read","sap_query","sap_create","sap_update","sap_delete","sap_function","sap_batch"],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"]},Yn=new Set(["sap_create","sap_update","sap_delete","sap_function","sap_batch","sap_idoc_send","sap_rfc_call","sap_bapi_call","sap_bapi_commit"]),Jn=["core","odata"],Xn=new Map;for(let[t,e]of Object.entries($t))for(let n of e)Xn.set(n,t);var at=class{handles=new Map;activeTiers;options;constructor(e){this.options=e,this.activeTiers=new Set(e.activeTiers);}registerHandle(e,n){this.handles.set(e,n);}applyInitialVisibility(){for(let[e,n]of this.handles)this.shouldToolBeVisible(e)||n.disable();}enableTier(e){if(this.options.allowedTiers&&!this.options.allowedTiers.includes(e))return {error:`Cannot enable ${e} tier: not permitted by the token policy of this connection (allowed: ${this.options.allowedTiers.join(", ")})`};if(e==="idoc"&&!this.options.hasIdocConfig)return {error:"Cannot enable IDoc tier: no IDoc partner configuration provided (--sndprn, --rcvprn required)"};if(e==="rfc"&&!this.options.hasRfcConfig)return {error:"Cannot enable RFC tier: no RFC connection configuration provided (SAP_RFC_ASHOST + SAP_RFC_SYSNR, or SAP_RFC_MSHOST + SAP_RFC_GROUP required)"};this.activeTiers.add(e);let n=$t[e],r=[];for(let s of n){if(this.options.readOnly&&Yn.has(s))continue;let i=this.handles.get(s);i&&(i.enable(),r.push(s));}return {enabled:r,activeTiers:this.getActiveTiers()}}getActiveTiers(){return Array.from(this.activeTiers)}getAllowedTiers(){return this.options.allowedTiers?[...this.options.allowedTiers]:void 0}getToolsByTier(e){return $t[e]}isReadOnlyMode(){return this.options.readOnly}shouldToolBeVisible(e){let n=Xn.get(e);return n?!(!this.activeTiers.has(n)||this.options.allowedTiers&&!this.options.allowedTiers.includes(n)||n==="idoc"&&!this.options.hasIdocConfig||n==="rfc"&&!this.options.hasRfcConfig||this.options.readOnly&&Yn.has(e)):true}};var Qn="/sap/opu/odata/sap",ao=/^[A-Z0-9_/]+$/i,J=class extends Error{guidance;constructor(e){super(`serviceUrl ist weder ein Pfad noch ein technischer Servicename: "${e}"`),this.name="InvalidServiceUrlError",this.guidance=`Erwartet wird ein Pfad wie ${Qn}/API_BUSINESS_PARTNER oder der technische Name (API_BUSINESS_PARTNER), den sap_discover_services als technicalName liefert.`;}};function Ee(t){let e=t.trim();if(e.length===0)throw new J(t);if(e.startsWith("/"))return e.replace(/\/+$/,"")||"/";if(e.startsWith("http://")||e.startsWith("https://"))try{return new URL(e).pathname.replace(/\/+$/,"")}catch{throw new J(t)}if(ao.test(e))return `${Qn}/${e.replace(/^\/+|\/+$/g,"")}`;throw new J(t)}var Re=class extends Error{guidance;extra;constructor(e,n,r={}){super(e),this.name="ToolInputError",this.guidance=n,this.extra=r;}};function P(t,e){if(t instanceof J)return Lt({code:"INVALID_INPUT",error:t.message,guidance:t.guidance,...e?.hint?{hint:e.hint}:{}});if(t instanceof Re)return Lt({code:"INVALID_INPUT",error:t.message,...t.guidance?{guidance:t.guidance}:{},...t.extra,...e?.hint?{hint:e.hint}:{}});let n=te(t,{serviceUrl:e?.serviceUrl});return Lt({code:n.code,error:n.message,...n.httpStatus!==void 0?{httpStatus:n.httpStatus}:{},...n.sapCode?{sapCode:n.sapCode}:{},...n.guidance?{guidance:n.guidance}:{},...e?.hint?{hint:e.hint}:{}})}function Lt(t){let e=Z()?.correlationId;return {content:[{type:"text",text:JSON.stringify({...t,...e?{correlationId:e}:{}},null,2)}],isError:true}}function Zn(t,e,n){let r=n??new E,s=new Map;function i(a){let c=s.get(a);return c||(c=new O(e,a,r),s.set(a,c)),c}function o(a,c){return P(a,{serviceUrl:c})}t.registerTool("sap_batch",{title:"Execute SAP Batch Request",description:"Execute multiple OData operations in a single batch request. Supports V2 (multipart/mixed) and V4 (JSON) batch formats automatically. Operations can be grouped into changesets for transactional behavior (all succeed or all fail). Returns per-operation status -- individual failures are always reported, never hidden behind the batch HTTP 200.",inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),operations:z$1.array(z$1.object({method:z$1.enum(["get","post","patch","delete"]).describe("HTTP method"),entitySet:z$1.string().describe("Entity set name"),key:z$1.record(z$1.string(),z$1.unknown()).optional().describe("Entity key for read/update/delete"),data:z$1.record(z$1.string(),z$1.unknown()).optional().describe("Request body for create/update"),changesetId:z$1.string().optional().describe("Changeset group ID for transactional grouping")})).describe("Array of operations to execute in batch")}),annotations:{readOnlyHint:false,destructiveHint:true,idempotentHint:false,openWorldHint:true}},async({serviceUrl:a,operations:c})=>{try{let l=i(a),u=c,d=await l.batch(u);return {content:[{type:"text",text:JSON.stringify(d,null,2)}]}}catch(l){return o(l)}});}async function er(t,e,n=5){if(t.length===0)return [];let r=Math.max(1,Math.min(Math.floor(n)||1,t.length)),s=new Array(t.length),i=0;async function o(){for(;;){let a=i++;if(a>=t.length)return;s[a]=await e(t[a],a);}}return await Promise.all(Array.from({length:r},()=>o())),s}var co=[".integrationsuite.","integrationsuite-trial.",".it-cpi.",".hci.",".prod.apimanagement.","-api.s4hana.ondemand.com"];function tr(t){let e=t.toLowerCase();return co.some(n=>e.includes(n))}var U=class extends Error{constructor(n,r,s,i){super(n);this.httpStatus=r;this.sapGuidance=s;this.isBtpDetected=i;this.name="CatalogError";}},lo=new Set(["ECONNREFUSED","ETIMEDOUT","ENOTFOUND"]);function uo(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 po(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 nr(t){return typeof t=="object"&&t!==null&&"response"in t&&typeof t.response=="object"&&t.response!==null&&typeof t.response.status=="number"}function mo(t){return typeof t=="object"&&t!==null&&"code"in t&&typeof t.code=="string"&&lo.has(t.code)}function Pe(t,e,n=false){let r=tr(e);if(nr(t)){let i=t.response.status,o=uo(i,n),a="message"in t&&typeof t.message=="string"?t.message:`HTTP ${i}`;return new U(a,i,o,r)}if(mo(t)){let i=po(t.code),o="message"in t&&typeof t.message=="string"?t.message:t.code;return new U(o,void 0,i,r)}let s=t instanceof Error?t.message:String(t);return new U(s,void 0,void 0,r)}function rr(t){return nr(t)&&t.response.status===404?"V4 catalog not available. Publish service group via /IWFND/V4_ADMIN.":t instanceof Error?t.message:String(t)}var fo=["/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/iwfnd/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/IWFND/CATALOGSERVICE/ServiceCollection"];function go(t){if(typeof t=="object"&&t!==null&&"response"in t)return t.response?.status}async function sr(t,e,n){let r;for(let s of fo)try{return await t.request({method:"get",url:s,params:{$format:"json",$top:"1"},expectedStatuses:[404]}),n?.onMatch?.(s),s}catch(i){if(i instanceof D)throw i;if(go(i)===404){r=i;continue}throw Pe(i,e,false)}throw Pe(r??new Error("All V2 catalog paths returned 404"),e,false)}function ir(t){try{return new URL(t).pathname}catch{return t}}function yo(t){let e=t.replace(/'/g,"''");return `substringof('${e}',TechnicalServiceName) or substringof('${e}',Title)`}var ct=class t{constructor(e,n,r={}){this.httpClient=e;this.cache=n;this.options=r;}static DEFAULT_V4_URL="/sap/opu/odata4/iwfnd/config/default/iwfnd/catalog/0002/ServiceGroups";resolvedV2Path;async discoverServices(e){let n=this.cache.get();if(n)return {services:n,warnings:[]};if(e){let l=await this.tryFilteredFetch(e);if(l)return l}let r=[],[s,i]=await er(["v4","v2"],async l=>l==="v4"?await this.fetchV4Catalog().then(u=>({ok:true,services:u}),u=>({ok:false,error:u})):await this.fetchV2Catalog().then(u=>({ok:true,services:u}),u=>({ok:false,error:u})),this.options.concurrency??5),o=[];if(s?.ok)o=s.services;else if(s){if(s.error instanceof D)throw s.error;r.push(`V4 catalog fetch failed: ${rr(s.error)}`);}if(!i?.ok){let l=i?.error;throw l instanceof U?l:Pe(l,this.options.baseUrl??"",false)}let a=i.services,c=this.mergeAndDeduplicate(o,a);return this.cache.set(c),{services:c,warnings:r}}async tryFilteredFetch(e){try{return {services:await this.fetchV2Catalog(yo(e)),warnings:[],filtered:!0}}catch(n){if(n instanceof D)throw n;n?.response?.status;return void 0}}async fetchV2Catalog(e){let n=this.options.catalogV2Url??this.resolvedV2Path;return n||(n=await sr(this.httpClient,this.options.baseUrl??"",{onMatch:this.options.onDiscoveryMatch}),this.resolvedV2Path=n),((await this.httpClient.request({method:"get",url:n,params:{$format:"json",...e?{$filter:e}:{}},...e?{expectedStatuses:[400,501]}:{}})).data?.d?.results??[]).map(o=>({technicalName:o.TechnicalServiceName,title:o.Title,serviceUrl:ir(o.ServiceUrl),description:o.Description,version:"v2",serviceVersion:o.TechnicalServiceVersion}))}async fetchV4Catalog(){let e=this.options.catalogV4Url??t.DEFAULT_V4_URL,s=(await this.httpClient.request({method:"get",url:e,params:{$expand:"DefaultSystem($expand=Services)"},headers:{Accept:"application/json"},expectedStatuses:[404]})).data?.value??[],i=[];for(let o of s){let a=o.DefaultSystem?.Services??[];for(let c of a)i.push({technicalName:c.ServiceId??c.ServiceName??"",title:c.ServiceTitle??c.Title??"",serviceUrl:ir(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 So=[[/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"]],vo=[[/^[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 To(t){for(let[e,n]of So)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};for(let[e,n]of vo)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};return {...t,category:"unknown",categoryConfidence:"low"}}function or(t){return t.map(To)}function pe(t,e){let n=Number.isFinite(t)&&t>0?Math.floor(t):50,r=Number.isFinite(e)&&e>0?Math.floor(e):0;return {top:Math.min(n,1e3),skip:r}}function me(t){let{returned:e,totalCount:n,paging:r,hasMoreHint:s}=t,i=s??(n!==void 0?r.skip+e<n:e>=r.top),o={returned:e,hasMore:i,top:r.top,skip:r.skip};return n!==void 0&&(o.totalCount=n),i&&(o.nextSkip=r.skip+e),o}function ar(t,e,n,r){let s=r??new H,i=new ct(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.
154
+ `),o=`multipart/mixed; boundary=${r}`;return {body:i,contentType:o}}var zi=new Set(["EntityType","Property","NavigationProperty","Association","EntitySet","AssociationSet","PropertyRef","End","FunctionImport","Parameter"]),Ki=new XMLParser({ignoreAttributes:false,attributeNamePrefix:"@_",removeNSPrefix:true,processEntities:false,htmlEntities:false,isArray:t=>zi.has(t)});function It(t){return t===void 0?true:String(t).toLowerCase()!=="false"}function st(t,e){let r=Ki.parse(t).Edmx.DataServices.Schema,s=Array.isArray(r)?r[0]:r,i=s.EntityType??[],o=s.Association??[],a=s.EntityContainer,c=a?.EntitySet??[],l=new Map;for(let g of o)l.set(g["@_Name"],g);let u=i.map(g=>{let y=Gi(g.Property??[]),h=(g.Key?.PropertyRef??[]).map(k=>k["@_Name"]),S=y.filter(k=>h.includes(k.name)),v=Yi(g.NavigationProperty??[],l);return {name:g["@_Name"],keys:S,properties:y,navigationProperties:v}}),d=c.map(g=>({name:g["@_Name"],entityTypeName:Ot(g["@_EntityType"]),creatable:It(g["@_creatable"]),updatable:It(g["@_updatable"]),deletable:It(g["@_deletable"])})),m=a?.FunctionImport??[],p=Ji(Array.isArray(m)?m:[m]),f={version:"v2",serviceUrl:e,entityTypes:u,entitySets:d};return p.length>0&&(f.functionImports=p),f}function Gi(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 Yi(t,e){return t.map(n=>{let r=Ot(n["@_Relationship"]),s=e.get(r),i="Unknown",o=false;if(s){let a=s.End.find(c=>c["@_Role"]===n["@_ToRole"]);a&&(i=Ot(a["@_Type"]),o=a["@_Multiplicity"]==="*");}return {name:n["@_Name"],targetEntityType:i,isCollection:o}})}function Ji(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 Ot(t){let e=t.lastIndexOf(".");return e>=0?t.substring(e+1):t}var Xi=/^\/Date\((-?\d+)([+-]\d{4})?\)\/$/;function Ce(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 Ln(t){let e=Xi.exec(t);if(!e)return t;let n=Number(e[1]);return new Date(n).toISOString()}function J(t){if(t==null)return t;if(Array.isArray(t))return t.map(e=>J(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]=Ln(s):n[r]=J(s)));return n}return typeof t=="string"?Ln(t):t}var Fn=10,_e=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;}async read(e,n,r){let s=kt(r??{});if(n){let i=`${this.serviceUrl}/${ue(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=Ce(o.data);return {...a,results:a.results.map(c=>J(c))}}return this.fetchWithPagination(`${this.serviceUrl}/${e}`,s,r?.maxPages??Fn)}async query(e,n){let r=kt(n,true),s=n.maxPages??Fn;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=Ce(r.data);return s.results.length>0?J(s.results[0]):{}}async update(e,n,r){let s=`${this.serviceUrl}/${ue(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}/${ue(e,n)}`,s=this.etagCache.get(e,n)??"*";await this.httpClient.request({method:"delete",url:r,headers:{Accept:"application/json","If-Match":s}}),this.etagCache.update(e,n,void 0);}async functionImport(e,n,r){let s=(r??"POST").toLowerCase(),i={$format:"json"};if(n)for(let[l,u]of Object.entries(n))typeof u=="string"?i[l]=`'${u}'`:i[l]=String(u);let o=await this.httpClient.request({method:s,url:`${this.serviceUrl}/${e}`,headers:{Accept:"application/json"},params:i}),a=Ce(o.data);if(a.results.length>0)return J(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}=$n(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=Dn(i.data,o),c=[...e,...(n??[]).flatMap(l=>l.operations)];for(let l=0;l<a.length&&l<c.length;l++)a[l].entitySet=c[l].entitySet;return {results:a}}async getMetadata(){let e=await this.httpClient.request({method:"get",url:`${this.serviceUrl}/$metadata`,headers:{Accept:"application/xml"}});return st(e.data,this.serviceUrl)}async fetchWithPagination(e,n,r){let s=[],i,o=e,a=n,c=0,l=false;for(;o&&c<r;){if(c>0&&$e()){l=true;break}let m=(await this.httpClient.request({method:"get",url:o,headers:{Accept:"application/json"},params:a})).data,p=Ce(m),f=p.results.map(y=>J(y));s.push(...f),c===0&&p.count!==void 0&&(i=p.count),c++;let g=m?.d;g&&typeof g.__next=="string"?(o=g.__next,a=void 0):o=void 0;}let u={results:s};return i!==void 0&&(u.count=i),o&&(u.nextLink=o),l&&(u.truncated=true,u.truncationReason=`Nach ${c} Seite(n) abgebrochen: Zeitbudget des Tool-Aufrufs ersch\xF6pft oder Client hat die Verbindung verworfen. Mit skip=${s.length} weiterlesen.`),u}};function jn(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 Hn(t){return typeof t=="string"?`'${t}'`:String(t)}function B(t,e){let n=Object.entries(e);if(n.length===1){let[,s]=n[0];return `${t}(${Hn(s)})`}let r=n.map(([s,i])=>`${s}=${Hn(i)}`).join(",");return `${t}(${r})`}function it(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 Bn(t,e){let n=[];for(let s=0;s<e.length;s++){let i=e[s],o=i.key?B(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 Vn(t){let e=t.split(".");return e[e.length-1]}function Qi(t,e){return {name:t,type:e.$Type??"Edm.String",nullable:e.$Nullable??false,maxLength:e.$MaxLength,precision:e.$Precision,scale:e.$Scale}}function qn(t,e){let n=JSON.parse(t),r=[],s=[],i=[];for(let[a,c]of Object.entries(n)){if(a.startsWith("$"))continue;let l=c;for(let[u,d]of Object.entries(l)){let m=d;if(m.$Kind==="Action"||m.$Kind==="Function"){let p=m.$IsBound??false,f=m.$Parameter??[],g=p&&f.length>0?f[0].$Name:void 0,y=(p?f.slice(1):f).map(S=>({name:S.$Name,type:S.$Type??"Edm.String",...S.$Nullable===true?{nullable:true}:{}})),h=m.$ReturnType?m.$ReturnType.$Type:void 0;i.push({name:u,isBound:p,...g?{bindingParameter:g}:{},parameters:y,...h?{returnType:h}:{}});}else if(m.$Kind==="EntityType"){let p=[],f=[];for(let[h,S]of Object.entries(m)){if(h.startsWith("$"))continue;let v=S;v.$Kind==="NavigationProperty"?f.push({name:h,targetEntityType:Vn(v.$Type),isCollection:v.$Collection??false}):p.push(Qi(h,v));}let y=(m.$Key??[]).map(h=>{let S=p.find(v=>v.name===h);return S||{name:h,type:"Edm.String",nullable:false}});r.push({name:u,keys:y,properties:p,navigationProperties:f});}else if(m.$Kind==="EntityContainer")for(let[p,f]of Object.entries(m)){if(p.startsWith("$"))continue;let g=f;g.$Collection&&g.$Type&&s.push({name:p,entityTypeName:Vn(g.$Type),creatable:Nt(g,"InsertRestrictions","Insertable"),updatable:Nt(g,"UpdateRestrictions","Updatable"),deletable:Nt(g,"DeleteRestrictions","Deletable")});}}}let o={version:"v4",serviceUrl:e,entityTypes:r,entitySets:s};return i.length>0&&(o.actions=i),o}function Nt(t,e,n){for(let[r,s]of Object.entries(t)){if(!r.startsWith("@")||!r.endsWith(e))continue;let i=s?.[n];if(typeof i=="boolean")return i}return true}var Zi=10,ot=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.etagCache=r;}async read(e,n,r){if(n){let l=`${this.serviceUrl}/${B(e,n)}`,u=r?it(r):void 0,d=await this.httpClient.request({method:"get",url:l,params:u}),m=d.data,p=d.headers.etag??d.headers.ETag;return p&&this.etagCache.set(e,n,p),{results:[m]}}let s=r?it(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??Zi,s=it(n,true),o=(await this.httpClient.request({method:"get",url:`${this.serviceUrl}/${e}`,params:s})).data,a=[...o.value??[]],c=o["@odata.count"],l=o["@odata.nextLink"],u=1,d=false;for(;l&&u<r;){if($e()){d=true;break}let p=this.resolveNextLink(l),g=(await this.httpClient.request({method:"get",url:p})).data,y=g.value??[];a.push(...y),l=g["@odata.nextLink"],u++;}let m={results:a,count:c};return l&&(m.nextLink=l),d&&(m.truncated=true,m.truncationReason=`Nach ${u} Seite(n) abgebrochen: Zeitbudget des Tool-Aufrufs ersch\xF6pft oder Client hat die Verbindung verworfen. Mit skip=${a.length} weiterlesen.`),m}async create(e,n){return (await this.httpClient.request({method:"post",url:`${this.serviceUrl}/${e}`,data:n,headers:{"Content-Type":"application/json"}})).data}async update(e,n,r){let s=`${this.serviceUrl}/${B(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}/${B(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}=Bn(this.serviceUrl,e),s=await this.httpClient.request({method:"post",url:`${this.serviceUrl}/$batch`,data:n,headers:{"Content-Type":r,Accept:"application/json"}}),i=jn(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}/${B(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}/${B(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 qn(n,this.serviceUrl)}resolveNextLink(e){return e.startsWith("http://")||e.startsWith("https://")?e:`${this.serviceUrl}/${e}`}};function Dt(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 O=class{constructor(e,n,r){this.httpClient=e;this.serviceUrl=n;this.metadataCache=r??new E;}protocolClient=null;version=null;initPromise=null;etagCache=new rt;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=Dt(n,this.serviceUrl),r=!0;}catch{this.version=Dt("",this.serviceUrl);}if(this.protocolClient=this.createProtocolClient(this.version),r&&this.version==="v2"&&n)try{let s=st(n,this.serviceUrl);this.metadataCache.set(this.serviceUrl,s);}catch{}}createProtocolClient(e){return e==="v4"?new ot(this.httpClient,this.serviceUrl,this.etagCache):new _e(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 _e)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 Wn(t,e,n){let r=n??new E,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),u=await new O(e,a,r).getMetadata(),d=u.entitySets.find(y=>y.name===c);if(!d)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity set '${c}' not found in service ${a}`,availableEntitySets:u.entitySets.map(y=>y.name)},null,2)}]};let m=u.entityTypes.find(y=>y.name===d.entityTypeName);if(!m)return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({error:`Entity type '${d.entityTypeName}' not found in metadata`},null,2)}]};let p=new Set(m.keys.map(y=>y.name)),f=m.properties.map(y=>({name:y.name,type:y.type,isKey:p.has(y.name)})),g=to(m.properties,u.version);return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({entitySet:c,version:u.version,properties:f,sampleFilter:g},null,2)}]}});}function to(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 zn(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 Kn(t,e,n){let r=n??new E,s=new ResourceTemplate("sap://metadata/{serviceUrl}",{list:void 0});t.registerResource("sap-metadata",s,{title:"SAP Service Metadata",description:"Entity types, properties, keys, and navigation properties for an SAP OData service",mimeType:"application/json"},async(i,o)=>{let a=decodeURIComponent(o.serviceUrl),l=await new O(e,a,r).getMetadata();return {contents:[{uri:i.toString(),mimeType:"application/json",text:JSON.stringify({version:l.version,serviceUrl:l.serviceUrl,entityTypes:l.entityTypes,entitySets:l.entitySets},null,2)}]}});}var xt={"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 so={$TODAY:()=>Mt(new Date),$YESTERDAY:()=>{let t=new Date;return t.setDate(t.getDate()-1),Mt(t)},$MONTH_START:()=>{let t=new Date;return `${t.getFullYear()}-${Ut(t.getMonth()+1)}-01`},$YEAR_START:()=>`${new Date().getFullYear()}-01-01`,$MONTH_AGO:()=>{let t=new Date;return t.setMonth(t.getMonth()-1),Mt(t)}};function Mt(t){return `${t.getFullYear()}-${Ut(t.getMonth()+1)}-${Ut(t.getDate())}`}function Ut(t){return t.toString().padStart(2,"0")}function io(t){if(typeof t!="string")return t;let e=so[t];return e?e():t}function Gn(t){return {...t,queryTemplates:t.queryTemplates.map(e=>({...e,filters:e.filters.map(n=>({...n,value:io(n.value)}))}))}}function Yn(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=xt[s];if(!i)return {contents:[{uri:n.toString(),mimeType:"application/json",text:JSON.stringify({error:`Unknown domain '${s}'`,availableDomains:Object.keys(xt)},null,2)}]};let o=Gn(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 $t={core:["test-connection","sap_list_services","sap_get_metadata","sap_discover_services","sap_nl_query"],odata:["sap_read","sap_query","sap_create","sap_update","sap_delete","sap_function","sap_batch"],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"]},Jn=new Set(["sap_create","sap_update","sap_delete","sap_function","sap_batch","sap_idoc_send","sap_rfc_call","sap_bapi_call","sap_bapi_commit"]),Xn=["core","odata"],Qn=new Map;for(let[t,e]of Object.entries($t))for(let n of e)Qn.set(n,t);var at=class{handles=new Map;activeTiers;options;constructor(e){this.options=e,this.activeTiers=new Set(e.activeTiers);}registerHandle(e,n){this.handles.set(e,n);}applyInitialVisibility(){for(let[e,n]of this.handles)this.shouldToolBeVisible(e)||n.disable();}enableTier(e){if(this.options.allowedTiers&&!this.options.allowedTiers.includes(e))return {error:`Cannot enable ${e} tier: not permitted by the token policy of this connection (allowed: ${this.options.allowedTiers.join(", ")})`};if(e==="idoc"&&!this.options.hasIdocConfig)return {error:"Cannot enable IDoc tier: no IDoc partner configuration provided (--sndprn, --rcvprn required)"};if(e==="rfc"&&!this.options.hasRfcConfig)return {error:"Cannot enable RFC tier: no RFC connection configuration provided (SAP_RFC_ASHOST + SAP_RFC_SYSNR, or SAP_RFC_MSHOST + SAP_RFC_GROUP required)"};this.activeTiers.add(e);let n=$t[e],r=[];for(let s of n){if(this.options.readOnly&&Jn.has(s))continue;let i=this.handles.get(s);i&&(i.enable(),r.push(s));}return {enabled:r,activeTiers:this.getActiveTiers()}}getActiveTiers(){return Array.from(this.activeTiers)}getAllowedTiers(){return this.options.allowedTiers?[...this.options.allowedTiers]:void 0}getToolsByTier(e){return $t[e]}isReadOnlyMode(){return this.options.readOnly}shouldToolBeVisible(e){let n=Qn.get(e);return n?!(!this.activeTiers.has(n)||this.options.allowedTiers&&!this.options.allowedTiers.includes(n)||n==="idoc"&&!this.options.hasIdocConfig||n==="rfc"&&!this.options.hasRfcConfig||this.options.readOnly&&Jn.has(e)):true}};var Zn="/sap/opu/odata/sap",ao=/^[A-Z0-9_/]+$/i,X=class extends Error{guidance;constructor(e){super(`serviceUrl ist weder ein Pfad noch ein technischer Servicename: "${e}"`),this.name="InvalidServiceUrlError",this.guidance=`Erwartet wird ein Pfad wie ${Zn}/API_BUSINESS_PARTNER oder der technische Name (API_BUSINESS_PARTNER), den sap_discover_services als technicalName liefert.`;}};function Ee(t){let e=t.trim();if(e.length===0)throw new X(t);if(e.startsWith("/"))return e.replace(/\/+$/,"")||"/";if(e.startsWith("http://")||e.startsWith("https://"))try{return new URL(e).pathname.replace(/\/+$/,"")}catch{throw new X(t)}if(ao.test(e))return `${Zn}/${e.replace(/^\/+|\/+$/g,"")}`;throw new X(t)}var Re=class extends Error{guidance;extra;constructor(e,n,r={}){super(e),this.name="ToolInputError",this.guidance=n,this.extra=r;}};function P(t,e){if(t instanceof X)return Lt({code:"INVALID_INPUT",error:t.message,guidance:t.guidance,...e?.hint?{hint:e.hint}:{}});if(t instanceof Re)return Lt({code:"INVALID_INPUT",error:t.message,...t.guidance?{guidance:t.guidance}:{},...t.extra,...e?.hint?{hint:e.hint}:{}});let n=ne(t,{serviceUrl:e?.serviceUrl});return Lt({code:n.code,error:n.message,...n.httpStatus!==void 0?{httpStatus:n.httpStatus}:{},...n.sapCode?{sapCode:n.sapCode}:{},...n.guidance?{guidance:n.guidance}:{},...e?.hint?{hint:e.hint}:{}})}function Lt(t){let e=ee()?.correlationId;return {content:[{type:"text",text:JSON.stringify({...t,...e?{correlationId:e}:{}},null,2)}],isError:true}}function er(t,e,n){let r=n??new E,s=new Map;function i(a){let c=s.get(a);return c||(c=new O(e,a,r),s.set(a,c)),c}function o(a,c){return P(a,{serviceUrl:c})}t.registerTool("sap_batch",{title:"Execute SAP Batch Request",description:"Execute multiple OData operations in a single batch request. Supports V2 (multipart/mixed) and V4 (JSON) batch formats automatically. Operations can be grouped into changesets for transactional behavior (all succeed or all fail). Returns per-operation status -- individual failures are always reported, never hidden behind the batch HTTP 200.",inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),operations:z$1.array(z$1.object({method:z$1.enum(["get","post","patch","delete"]).describe("HTTP method"),entitySet:z$1.string().describe("Entity set name"),key:z$1.record(z$1.string(),z$1.unknown()).optional().describe("Entity key for read/update/delete"),data:z$1.record(z$1.string(),z$1.unknown()).optional().describe("Request body for create/update"),changesetId:z$1.string().optional().describe("Changeset group ID for transactional grouping")})).describe("Array of operations to execute in batch")}),annotations:{readOnlyHint:false,destructiveHint:true,idempotentHint:false,openWorldHint:true}},async({serviceUrl:a,operations:c})=>{try{let l=i(a),u=c,d=await l.batch(u);return {content:[{type:"text",text:JSON.stringify(d,null,2)}]}}catch(l){return o(l)}});}async function tr(t,e,n=5){if(t.length===0)return [];let r=Math.max(1,Math.min(Math.floor(n)||1,t.length)),s=new Array(t.length),i=0;async function o(){for(;;){let a=i++;if(a>=t.length)return;s[a]=await e(t[a],a);}}return await Promise.all(Array.from({length:r},()=>o())),s}var co=[".integrationsuite.","integrationsuite-trial.",".it-cpi.",".hci.",".prod.apimanagement.","-api.s4hana.ondemand.com"];function nr(t){let e=t.toLowerCase();return co.some(n=>e.includes(n))}var U=class extends Error{constructor(n,r,s,i){super(n);this.httpStatus=r;this.sapGuidance=s;this.isBtpDetected=i;this.name="CatalogError";}},lo=new Set(["ECONNREFUSED","ETIMEDOUT","ENOTFOUND"]);function uo(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 po(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 rr(t){return typeof t=="object"&&t!==null&&"response"in t&&typeof t.response=="object"&&t.response!==null&&typeof t.response.status=="number"}function mo(t){return typeof t=="object"&&t!==null&&"code"in t&&typeof t.code=="string"&&lo.has(t.code)}function Pe(t,e,n=false){let r=nr(e);if(rr(t)){let i=t.response.status,o=uo(i,n),a="message"in t&&typeof t.message=="string"?t.message:`HTTP ${i}`;return new U(a,i,o,r)}if(mo(t)){let i=po(t.code),o="message"in t&&typeof t.message=="string"?t.message:t.code;return new U(o,void 0,i,r)}let s=t instanceof Error?t.message:String(t);return new U(s,void 0,void 0,r)}function sr(t){return rr(t)&&t.response.status===404?"V4 catalog not available. Publish service group via /IWFND/V4_ADMIN.":t instanceof Error?t.message:String(t)}var fo=["/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/iwfnd/CATALOGSERVICE;v=2/ServiceCollection","/sap/opu/odata/IWFND/CATALOGSERVICE/ServiceCollection"];function go(t){if(typeof t=="object"&&t!==null&&"response"in t)return t.response?.status}async function ir(t,e,n){let r;for(let s of fo)try{return await t.request({method:"get",url:s,params:{$format:"json",$top:"1"},expectedStatuses:[404]}),n?.onMatch?.(s),s}catch(i){if(i instanceof D)throw i;if(go(i)===404){r=i;continue}throw Pe(i,e,false)}throw Pe(r??new Error("All V2 catalog paths returned 404"),e,false)}function or(t){try{return new URL(t).pathname}catch{return t}}function yo(t){let e=t.replace(/'/g,"''");return `substringof('${e}',TechnicalServiceName) or substringof('${e}',Title)`}var ct=class t{constructor(e,n,r={}){this.httpClient=e;this.cache=n;this.options=r;}static DEFAULT_V4_URL="/sap/opu/odata4/iwfnd/config/default/iwfnd/catalog/0002/ServiceGroups";resolvedV2Path;async discoverServices(e){let n=this.cache.get();if(n)return {services:n,warnings:[]};if(e){let l=await this.tryFilteredFetch(e);if(l)return l}let r=[],[s,i]=await tr(["v4","v2"],async l=>l==="v4"?await this.fetchV4Catalog().then(u=>({ok:true,services:u}),u=>({ok:false,error:u})):await this.fetchV2Catalog().then(u=>({ok:true,services:u}),u=>({ok:false,error:u})),this.options.concurrency??5),o=[];if(s?.ok)o=s.services;else if(s){if(s.error instanceof D)throw s.error;r.push(`V4 catalog fetch failed: ${sr(s.error)}`);}if(!i?.ok){let l=i?.error;throw l instanceof U?l:Pe(l,this.options.baseUrl??"",false)}let a=i.services,c=this.mergeAndDeduplicate(o,a);return this.cache.set(c),{services:c,warnings:r}}async tryFilteredFetch(e){try{return {services:await this.fetchV2Catalog(yo(e)),warnings:[],filtered:!0}}catch(n){if(n instanceof D)throw n;n?.response?.status;return void 0}}async fetchV2Catalog(e){let n=this.options.catalogV2Url??this.resolvedV2Path;return n||(n=await ir(this.httpClient,this.options.baseUrl??"",{onMatch:this.options.onDiscoveryMatch}),this.resolvedV2Path=n),((await this.httpClient.request({method:"get",url:n,params:{$format:"json",...e?{$filter:e}:{}},...e?{expectedStatuses:[400,501]}:{}})).data?.d?.results??[]).map(o=>({technicalName:o.TechnicalServiceName,title:o.Title,serviceUrl:or(o.ServiceUrl),description:o.Description,version:"v2",serviceVersion:o.TechnicalServiceVersion}))}async fetchV4Catalog(){let e=this.options.catalogV4Url??t.DEFAULT_V4_URL,s=(await this.httpClient.request({method:"get",url:e,params:{$expand:"DefaultSystem($expand=Services)"},headers:{Accept:"application/json"},expectedStatuses:[404]})).data?.value??[],i=[];for(let o of s){let a=o.DefaultSystem?.Services??[];for(let c of a)i.push({technicalName:c.ServiceId??c.ServiceName??"",title:c.ServiceTitle??c.Title??"",serviceUrl:or(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 So=[[/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"]],vo=[[/^[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 bo(t){for(let[e,n]of So)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};for(let[e,n]of vo)if(e.test(t.technicalName))return {...t,category:n,categoryConfidence:"high"};return {...t,category:"unknown",categoryConfidence:"low"}}function ar(t){return t.map(bo)}function pe(t,e){let n=Number.isFinite(t)&&t>0?Math.floor(t):50,r=Number.isFinite(e)&&e>0?Math.floor(e):0;return {top:Math.min(n,1e3),skip:r}}function me(t){let{returned:e,totalCount:n,paging:r,hasMoreHint:s}=t,i=s??(n!==void 0?r.skip+e<n:e>=r.top),o={returned:e,hasMore:i,top:r.top,skip:r.skip};return n!==void 0&&(o.totalCount=n),i&&(o.nextSkip=r.skip+e),o}function cr(t,e,n,r){let s=r??new H,i=new ct(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.
155
155
 
156
156
  **When to use:**
157
157
  - You need to find which SAP services are available on this system
@@ -160,7 +160,7 @@ ${u}`);}s.push(`--${r}--`);let i=s.join(`\r
160
160
 
161
161
  **When NOT to use:**
162
162
  - You already know the service URL (proceed to sap_query or sap_nl_query)
163
- - You need entity-level metadata (use sap_get_metadata after discovering the service)`,inputSchema:z$1.object({search:z$1.string().optional().describe("Free-text search across service name, title, and description (case-insensitive)"),category:z$1.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$1.number().optional().default(50).describe(`Maximum number of services to return (default ${50}, hard cap ${1e3})`),offset:z$1.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:l})=>{try{let u=await i.discoverServices(o),d=or(u.services);if(a&&(d=d.filter(S=>S.category===a)),o&&!u.filtered){let S=o.toLowerCase();d=d.filter(v=>v.technicalName.toLowerCase().includes(S)||v.title.toLowerCase().includes(S)||(v.description?.toLowerCase().includes(S)??!1));}let m=d.length,p={};for(let S of d)p[S.category]=(p[S.category]??0)+1;let f=pe(c,l),g=d.slice(f.skip,f.skip+f.top),y=me({returned:g.length,totalCount:m,paging:f}),h={services:g.map(S=>({technicalName:S.technicalName,title:S.title,category:S.category,serviceUrl:S.serviceUrl,version:S.version})),...y,summary:{total:m,returned:g.length,offset:f.skip,byCategory:p},...u.warnings.length>0?{warnings:u.warnings}:{},...o?{filterApplied:u.filtered?"server":"client"}:{}};return {content:[{type:"text",text:JSON.stringify(h,null,2)}]}}catch(u){if(u instanceof U){let d={error:u.message};return u.sapGuidance&&(d.guidance=u.sapGuidance),u.isBtpDetected&&(d.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(d,null,2)}],isError:true}}return P(u)}});}function cr(t,e,n){let r=new E;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.
163
+ - You need entity-level metadata (use sap_get_metadata after discovering the service)`,inputSchema:z$1.object({search:z$1.string().optional().describe("Free-text search across service name, title, and description (case-insensitive)"),category:z$1.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$1.number().optional().default(50).describe(`Maximum number of services to return (default ${50}, hard cap ${1e3})`),offset:z$1.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:l})=>{try{let u=await i.discoverServices(o),d=ar(u.services);if(a&&(d=d.filter(S=>S.category===a)),o&&!u.filtered){let S=o.toLowerCase();d=d.filter(v=>v.technicalName.toLowerCase().includes(S)||v.title.toLowerCase().includes(S)||(v.description?.toLowerCase().includes(S)??!1));}let m=d.length,p={};for(let S of d)p[S.category]=(p[S.category]??0)+1;let f=pe(c,l),g=d.slice(f.skip,f.skip+f.top),y=me({returned:g.length,totalCount:m,paging:f}),h={services:g.map(S=>({technicalName:S.technicalName,title:S.title,category:S.category,serviceUrl:S.serviceUrl,version:S.version})),...y,summary:{total:m,returned:g.length,offset:f.skip,byCategory:p},...u.warnings.length>0?{warnings:u.warnings}:{},...o?{filterApplied:u.filtered?"server":"client"}:{}};return {content:[{type:"text",text:JSON.stringify(h,null,2)}]}}catch(u){if(u instanceof U){let d={error:u.message};return u.sapGuidance&&(d.guidance=u.sapGuidance),u.isBtpDetected&&(d.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(d,null,2)}],isError:true}}return P(u)}});}function lr(t,e,n){let r=new E;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.
164
164
 
165
165
  **When to use:**
166
166
  - You need the raw list of OData services at a base URL
@@ -179,14 +179,14 @@ Each entity set carries what SAP allows on it \u2014 \`creatable\`, \`updatable\
179
179
  - Use 'search' to filter large services by name pattern
180
180
 
181
181
  **When NOT to use:**
182
- - You don't know which service to query (use sap_discover_services first)`,inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),entityType:z$1.string().optional().describe("Filter to a specific entity type name for full property details"),search:z$1.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 O(e,Ee(s),r).getMetadata(),l;if(i){let u=c.entityTypes.find(d=>d.name===i);if(!u){let d=i.toLowerCase();u=c.entityTypes.find(m=>m.name.toLowerCase()===d);}if(!u){let d=i.toLowerCase(),m=c.entityTypes.filter(p=>p.name.toLowerCase().includes(d)).map(p=>p.name).slice(0,20);return P(new Re(`Entity type '${i}' existiert in diesem Service nicht.`,"Mit dem Parameter 'search' nach passenden Typen suchen, oder entityType weglassen, um alle Entity Sets zu sehen.",m.length>0?{closestMatches:m}:{}),{serviceUrl:s})}l={version:c.version,entityType:u};}else {let u=c.entitySets.filter(f=>W(f.name,n)),d=c.entityTypes;if(o){let f=o.toLowerCase();u=u.filter(g=>g.name.toLowerCase().includes(f)),d=d.filter(g=>g.name.toLowerCase().includes(f));}let m={creatable:u.filter(f=>f.creatable).map(f=>f.name),updatable:u.filter(f=>f.updatable).length,deletable:u.filter(f=>f.deletable).length};d.length>50&&!o?l={version:c.version,entitySets:u,capabilities:m,entityTypeCount:d.length,hint:`Service has ${d.length} entity types. Use the 'search' parameter (e.g. search='BusinessPartner') or 'entityType' parameter to get details for specific types. Entity sets listed above are queryable.`}:l={version:c.version,entityTypes:d.map(f=>({name:f.name,keys:f.keys.map(g=>g.name),propertyCount:f.properties.length,navigationProperties:f.navigationProperties.map(g=>g.name)})),entitySets:u,capabilities:m};}return {content:[{type:"text",text:JSON.stringify(l,null,2)}]}}catch(a){return P(a,{serviceUrl:s})}});}function lr(t,e,n){let r=n??new E,s=new Map;function i(a){let c=s.get(a);return c||(c=new O(e,a,r),s.set(a,c)),c}function o(a,c){return P(a,{serviceUrl:c})}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$1.object({serviceUrl:z$1.string().describe("OData service URL"),functionName:z$1.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$1.record(z$1.string(),z$1.unknown()).optional().describe("Function/action parameters as key-value pairs"),httpMethod:z$1.enum(["GET","POST"]).optional().describe("HTTP method override. V2: from metadata or default POST. V4: Actions=POST, Functions=GET"),entitySet:z$1.string().optional().describe("Entity set for V4 bound actions/functions"),key:z$1.record(z$1.string(),z$1.unknown()).optional().describe('Entity key for V4 bound actions/functions, e.g. { BusinessPartner: "1000001" }'),isFunction:z$1.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:l,httpMethod:u,entitySet:d,key:m,isFunction:p})=>{try{let g=await i(a).functionImport(c,l,{httpMethod:u,entitySet:d,key:m,isFunction:p??!1});return {content:[{type:"text",text:JSON.stringify(g,null,2)}]}}catch(f){return o(f)}});}function dr(t,e,n){t.registerTool("test-connection",{title:"Test SAP Connection",description:`Diagnose the connection to the configured SAP system stage by stage: DNS \u2192 TLS (including certificate chain) \u2192 authentication \u2192 client (Mandant) \u2192 service catalog. Reports the FIRST failing stage with a concrete next step, so a 401 no longer leaves you guessing between certificate, user and client.
182
+ - You don't know which service to query (use sap_discover_services first)`,inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),entityType:z$1.string().optional().describe("Filter to a specific entity type name for full property details"),search:z$1.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 O(e,Ee(s),r).getMetadata(),l;if(i){let u=c.entityTypes.find(d=>d.name===i);if(!u){let d=i.toLowerCase();u=c.entityTypes.find(m=>m.name.toLowerCase()===d);}if(!u){let d=i.toLowerCase(),m=c.entityTypes.filter(p=>p.name.toLowerCase().includes(d)).map(p=>p.name).slice(0,20);return P(new Re(`Entity type '${i}' existiert in diesem Service nicht.`,"Mit dem Parameter 'search' nach passenden Typen suchen, oder entityType weglassen, um alle Entity Sets zu sehen.",m.length>0?{closestMatches:m}:{}),{serviceUrl:s})}l={version:c.version,entityType:u};}else {let u=c.entitySets.filter(f=>W(f.name,n)),d=c.entityTypes;if(o){let f=o.toLowerCase();u=u.filter(g=>g.name.toLowerCase().includes(f)),d=d.filter(g=>g.name.toLowerCase().includes(f));}let m={creatable:u.filter(f=>f.creatable).map(f=>f.name),updatable:u.filter(f=>f.updatable).length,deletable:u.filter(f=>f.deletable).length};d.length>50&&!o?l={version:c.version,entitySets:u,capabilities:m,entityTypeCount:d.length,hint:`Service has ${d.length} entity types. Use the 'search' parameter (e.g. search='BusinessPartner') or 'entityType' parameter to get details for specific types. Entity sets listed above are queryable.`}:l={version:c.version,entityTypes:d.map(f=>({name:f.name,keys:f.keys.map(g=>g.name),propertyCount:f.properties.length,navigationProperties:f.navigationProperties.map(g=>g.name)})),entitySets:u,capabilities:m};}return {content:[{type:"text",text:JSON.stringify(l,null,2)}]}}catch(a){return P(a,{serviceUrl:s})}});}function dr(t,e,n){let r=n??new E,s=new Map;function i(a){let c=s.get(a);return c||(c=new O(e,a,r),s.set(a,c)),c}function o(a,c){return P(a,{serviceUrl:c})}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$1.object({serviceUrl:z$1.string().describe("OData service URL"),functionName:z$1.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$1.record(z$1.string(),z$1.unknown()).optional().describe("Function/action parameters as key-value pairs"),httpMethod:z$1.enum(["GET","POST"]).optional().describe("HTTP method override. V2: from metadata or default POST. V4: Actions=POST, Functions=GET"),entitySet:z$1.string().optional().describe("Entity set for V4 bound actions/functions"),key:z$1.record(z$1.string(),z$1.unknown()).optional().describe('Entity key for V4 bound actions/functions, e.g. { BusinessPartner: "1000001" }'),isFunction:z$1.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:l,httpMethod:u,entitySet:d,key:m,isFunction:p})=>{try{let g=await i(a).functionImport(c,l,{httpMethod:u,entitySet:d,key:m,isFunction:p??!1});return {content:[{type:"text",text:JSON.stringify(g,null,2)}]}}catch(f){return o(f)}});}function ur(t,e,n){t.registerTool("test-connection",{title:"Test SAP Connection",description:`Diagnose the connection to the configured SAP system stage by stage: DNS \u2192 TLS (including certificate chain) \u2192 authentication \u2192 client (Mandant) \u2192 service catalog. Reports the FIRST failing stage with a concrete next step, so a 401 no longer leaves you guessing between certificate, user and client.
183
183
 
184
184
  **When to use:**
185
185
  - Any SAP tool fails and you need to know which layer is at fault
186
186
  - After changing SAP_BASE_URL, credentials, SAP_CLIENT or the CA bundle
187
187
 
188
188
  **When NOT to use:**
189
- - You already know the connection works (proceed to sap_discover_services)`,inputSchema:z$1.object({}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async()=>{try{let r=await je(e,{baseUrl:n?.baseUrl??"",sapClient:n?.sapClient,tlsVerify:n?.tlsVerify});return {content:[{type:"text",text:JSON.stringify({...r,...r.status==="connected"?{hint:"N\xE4chster Schritt: sap_discover_services zeigt die verf\xFCgbaren OData-Services dieses Systems."}:{}},null,2)}],...r.status==="failed"?{isError:!0}:{}}}catch(r){return P(r,{hint:"Die Verbindungsdiagnose konnte nicht ausgef\xFChrt werden. Das ist ein Fehler des Servers, kein Befund \xFCber das SAP-System."})}});}function wo(t){return {name:t.FieldName,dataType:t.DataType,length:t.Length,decimals:t.Decimals??0,required:t.Required}}function Ao(t){return {name:t.SegmentName,parentSegment:t.ParentSegment??"",minOccurrence:t.MinOccurrence,maxOccurrence:t.MaxOccurrence,fields:t.Fields?.results?.map(wo)??[]}}function Co(t){return {idocType:t.IdocType,mesType:t.MesType,segments:t.Segments?.results?.map(Ao)??[]}}async function Ft(t,e,n){try{if(n){let o=(await t.request({method:"get",url:`${e}/IdocTypes('${n}')?$expand=Segments($expand=Fields)`})).data;return Co(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 ur={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 pr(t,e,n){try{let i=(await t.request({method:"get",url:`${e}/IdocStatus('${n}')`})).data.d,o=i.Status,a=ur[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 jt={name:z$1.string().describe("Segment name, e.g. E1MARAM"),fields:z$1.record(z$1.string(),z$1.string()).describe("Segment field values")},_o=z$1.object({...jt}),Eo=z$1.object({...jt,children:z$1.array(_o).optional().describe("Nested child segments (level 3)")}),Ro=z$1.object({...jt,children:z$1.array(Eo).optional().describe("Nested child segments")});function mr(t,e,n,r){function s(i,o){return P(i,{serviceUrl:o})}t.registerTool("sap_idoc_send",{title:"Send IDoc to SAP",description:"Send an IDoc to SAP via HTTP/XML. Auto-constructs EDI_DC40 control record. Validates against segment metadata before sending -- blocks on validation failure.",inputSchema:z$1.object({idocType:z$1.string().describe("IDoc type name, e.g. MATMAS05, ORDERS05"),segments:z$1.array(Ro).describe("IDoc data segments"),metadataServiceUrl:z$1.string().optional().describe("OData service URL for IDoc metadata validation. If provided, validates before sending."),mesType:z$1.string().optional().describe("Override auto-derived message type (MESTYP)"),sndpor:z$1.string().optional().describe("Override sender port"),sndprn:z$1.string().optional().describe("Override sender partner number"),rcvpor:z$1.string().optional().describe("Override receiver port"),rcvprn:z$1.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:l,sndprn:u,rcvpor:d,rcvprn:m})=>{try{let p;if(a){let g=await Ft(e,a,i);Array.isArray(g)||(p=g);}let f=await n.send({idocType:i,segments:o,metadata:p,partnerOverrides:{mesType:c,sndpor:l,sndprn:u,rcvpor:d,rcvprn:m}});return {content:[{type:"text",text:JSON.stringify(f,null,2)}]}}catch(p){return s(p)}}),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$1.object({docnum:z$1.string().describe("IDoc document number"),statusServiceUrl:z$1.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 pr(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$1.object({metadataServiceUrl:z$1.string().describe("OData service URL for IDoc metadata"),idocType:z$1.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 Ft(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$1.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 Po={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 gr(t,e){if(e.isReadOnlyMode())return;let n=e.getActiveTiers(),r=["core","odata","idoc","rfc"],s=e.getAllowedTiers(),i=r.filter(a=>!n.includes(a)&&(!s||s.includes(a))),o=i.length>0?`Available tiers to activate: ${i.map(a=>`${a} (${Po[a]})`).join(", ")}.`:"All tiers are already active.";t.registerTool("sap_enable_tools",{title:"Enable Additional SAP Tools",description:`Activate additional tool tiers for this session. Currently active: ${n.join(", ")}. ${o}`,inputSchema:z$1.object({tier:z$1.enum(["core","odata","idoc","rfc"]).describe('Tier to activate. Use "idoc" for IDoc tools, "rfc" for RFC/BAPI tools.')}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},async({tier:a})=>{let c=e.enableTier(a);return "error"in c?{content:[{type:"text",text:JSON.stringify({status:"error",message:c.error})}],isError:true}:{content:[{type:"text",text:JSON.stringify({status:"success",tier_enabled:a,enabled_tools:c.enabled,active_tiers:c.activeTiers,note:"Tools enabled. If your client did not refresh the tool list automatically, you may need to reconnect."})}]}});}function hr(t,e){return t.length===0?"":t.map(n=>ko(n,e)).join(" and ")}function ko(t,e){let{field:n,operator:r,value:s}=t;switch(r){case "contains":return Io(n,String(s),e);case "startswith":return `startswith(${n},'${lt(String(s))}')`;case "endswith":return `endswith(${n},'${lt(String(s))}')`;default:return Oo(n,r,s,e)}}function Io(t,e,n){let r=lt(e);return n==="v2"?`substringof('${r}',${t})`:`contains(${t},'${r}')`}function Oo(t,e,n,r){let s=No(n,r);return `${t} ${e} ${s}`}function No(t,e){return typeof t=="number"||typeof t=="boolean"?String(t):Do(t)?xo(t,e):`'${lt(t)}'`}function Do(t){return /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?)?/.test(t)}function xo(t,e){return e==="v2"?`datetime'${t.includes("T")?t:`${t}T00:00:00`}'`:t}function lt(t){return t.replace(/'/g,"''")}function yr(t,e,n,r){let s=r??new E,i=new Map;function o(c){let l=i.get(c);return l||(l=new O(e,c,s),i.set(c,l)),l}function a(c,l){return P(c,{serviceUrl:l})}t.registerTool("sap_nl_query",{title:"Execute NL-Generated OData Query",description:`Execute a structured OData query generated from natural language. Accepts structured filters (field/operator/value) and converts them to the correct OData V2 or V4 filter syntax automatically. Read-only: only performs queries, never creates, updates, or deletes data.
189
+ - You already know the connection works (proceed to sap_discover_services)`,inputSchema:z$1.object({}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async()=>{try{let r=await je(e,{baseUrl:n?.baseUrl??"",sapClient:n?.sapClient,tlsVerify:n?.tlsVerify});return {content:[{type:"text",text:JSON.stringify({...r,...r.status==="connected"?{hint:"N\xE4chster Schritt: sap_discover_services zeigt die verf\xFCgbaren OData-Services dieses Systems."}:{}},null,2)}],...r.status==="failed"?{isError:!0}:{}}}catch(r){return P(r,{hint:"Die Verbindungsdiagnose konnte nicht ausgef\xFChrt werden. Das ist ein Fehler des Servers, kein Befund \xFCber das SAP-System."})}});}function wo(t){return {name:t.FieldName,dataType:t.DataType,length:t.Length,decimals:t.Decimals??0,required:t.Required}}function Ao(t){return {name:t.SegmentName,parentSegment:t.ParentSegment??"",minOccurrence:t.MinOccurrence,maxOccurrence:t.MaxOccurrence,fields:t.Fields?.results?.map(wo)??[]}}function Co(t){return {idocType:t.IdocType,mesType:t.MesType,segments:t.Segments?.results?.map(Ao)??[]}}async function Ft(t,e,n){try{if(n){let o=(await t.request({method:"get",url:`${e}/IdocTypes('${n}')?$expand=Segments($expand=Fields)`})).data;return Co(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 pr={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 mr(t,e,n){try{let i=(await t.request({method:"get",url:`${e}/IdocStatus('${n}')`})).data.d,o=i.Status,a=pr[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 jt={name:z$1.string().describe("Segment name, e.g. E1MARAM"),fields:z$1.record(z$1.string(),z$1.string()).describe("Segment field values")},_o=z$1.object({...jt}),Eo=z$1.object({...jt,children:z$1.array(_o).optional().describe("Nested child segments (level 3)")}),Ro=z$1.object({...jt,children:z$1.array(Eo).optional().describe("Nested child segments")});function fr(t,e,n,r){function s(i,o){return P(i,{serviceUrl:o})}t.registerTool("sap_idoc_send",{title:"Send IDoc to SAP",description:"Send an IDoc to SAP via HTTP/XML. Auto-constructs EDI_DC40 control record. Validates against segment metadata before sending -- blocks on validation failure.",inputSchema:z$1.object({idocType:z$1.string().describe("IDoc type name, e.g. MATMAS05, ORDERS05"),segments:z$1.array(Ro).describe("IDoc data segments"),metadataServiceUrl:z$1.string().optional().describe("OData service URL for IDoc metadata validation. If provided, validates before sending."),mesType:z$1.string().optional().describe("Override auto-derived message type (MESTYP)"),sndpor:z$1.string().optional().describe("Override sender port"),sndprn:z$1.string().optional().describe("Override sender partner number"),rcvpor:z$1.string().optional().describe("Override receiver port"),rcvprn:z$1.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:l,sndprn:u,rcvpor:d,rcvprn:m})=>{try{let p;if(a){let g=await Ft(e,a,i);Array.isArray(g)||(p=g);}let f=await n.send({idocType:i,segments:o,metadata:p,partnerOverrides:{mesType:c,sndpor:l,sndprn:u,rcvpor:d,rcvprn:m}});return {content:[{type:"text",text:JSON.stringify(f,null,2)}]}}catch(p){return s(p)}}),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$1.object({docnum:z$1.string().describe("IDoc document number"),statusServiceUrl:z$1.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 mr(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$1.object({metadataServiceUrl:z$1.string().describe("OData service URL for IDoc metadata"),idocType:z$1.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 Ft(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$1.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 Po={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 hr(t,e){if(e.isReadOnlyMode())return;let n=e.getActiveTiers(),r=["core","odata","idoc","rfc"],s=e.getAllowedTiers(),i=r.filter(a=>!n.includes(a)&&(!s||s.includes(a))),o=i.length>0?`Available tiers to activate: ${i.map(a=>`${a} (${Po[a]})`).join(", ")}.`:"All tiers are already active.";t.registerTool("sap_enable_tools",{title:"Enable Additional SAP Tools",description:`Activate additional tool tiers for this session. Currently active: ${n.join(", ")}. ${o}`,inputSchema:z$1.object({tier:z$1.enum(["core","odata","idoc","rfc"]).describe('Tier to activate. Use "idoc" for IDoc tools, "rfc" for RFC/BAPI tools.')}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},async({tier:a})=>{let c=e.enableTier(a);return "error"in c?{content:[{type:"text",text:JSON.stringify({status:"error",message:c.error})}],isError:true}:{content:[{type:"text",text:JSON.stringify({status:"success",tier_enabled:a,enabled_tools:c.enabled,active_tiers:c.activeTiers,note:"Tools enabled. If your client did not refresh the tool list automatically, you may need to reconnect."})}]}});}function yr(t,e){return t.length===0?"":t.map(n=>ko(n,e)).join(" and ")}function ko(t,e){let{field:n,operator:r,value:s}=t;switch(r){case "contains":return Io(n,String(s),e);case "startswith":return `startswith(${n},'${lt(String(s))}')`;case "endswith":return `endswith(${n},'${lt(String(s))}')`;default:return Oo(n,r,s,e)}}function Io(t,e,n){let r=lt(e);return n==="v2"?`substringof('${r}',${t})`:`contains(${t},'${r}')`}function Oo(t,e,n,r){let s=No(n,r);return `${t} ${e} ${s}`}function No(t,e){return typeof t=="number"||typeof t=="boolean"?String(t):Do(t)?xo(t,e):`'${lt(t)}'`}function Do(t){return /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?)?/.test(t)}function xo(t,e){return e==="v2"?`datetime'${t.includes("T")?t:`${t}T00:00:00`}'`:t}function lt(t){return t.replace(/'/g,"''")}function Sr(t,e,n,r){let s=r??new E,i=new Map;function o(c){let l=i.get(c);return l||(l=new O(e,c,s),i.set(c,l)),l}function a(c,l){return P(c,{serviceUrl:l})}t.registerTool("sap_nl_query",{title:"Execute NL-Generated OData Query",description:`Execute a structured OData query generated from natural language. Accepts structured filters (field/operator/value) and converts them to the correct OData V2 or V4 filter syntax automatically. Read-only: only performs queries, never creates, updates, or deletes data.
190
190
 
191
191
  **When to use:**
192
192
  - You have a natural language question about SAP data
@@ -196,7 +196,7 @@ Each entity set carries what SAP allows on it \u2014 \`creatable\`, \`updatable\
196
196
  **When NOT to use:**
197
197
  - You need to create, update, or delete data (use sap_create/sap_update/sap_delete)
198
198
  - You already have an exact OData $filter string (use sap_query instead)
199
- - You don't know which service to query (use sap://routing/{domain} first)`,inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),entitySet:z$1.string().describe("Entity set to query"),filters:z$1.array(z$1.object({field:z$1.string().describe("Property name from entity schema"),operator:z$1.enum(["eq","ne","gt","ge","lt","le","contains","startswith","endswith"]).describe("Filter operator"),value:z$1.union([z$1.string(),z$1.number(),z$1.boolean()]).describe("Filter value")})).describe("Structured filters from NL interpretation"),select:z$1.array(z$1.string()).optional().describe("Properties to return"),orderby:z$1.string().optional().describe("Sort expression"),top:z$1.number().optional().describe("Maximum number of results"),skip:z$1.number().optional().describe("Number of results to skip")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:c,entitySet:l,filters:u,select:d,orderby:m,top:p,skip:f})=>{if(!W(l,n))return a(new Error(`Entity set '${l}' is not exposed. Configure --expose to include it.`));try{let g=o(c),h=(await g.getMetadata()).version,S=hr(u,h),v=await g.query(l,{filter:S||void 0,select:d,orderby:m,top:p,skip:f});return {content:[{type:"text",text:JSON.stringify({version:h,generatedFilter:S||"(none)",results:v.results,count:v.count,nextLink:v.nextLink},null,2)}]}}catch(g){return a(g)}});}function Sr(t,e,n,r){let s=r??new E,i=new Map;function o(l){let u=Ee(l),d=i.get(u);return d||(d=new O(e,u,s),i.set(u,d)),d}function a(l,u){return P(l,{serviceUrl:u})}function c(l){return W(l,n)?null:a(new Error(`Entity set '${l}' is not exposed. Configure --expose to include it.`))}t.registerTool("sap_read",{title:"Read SAP Entity",description:`Read a single entity or entity collection from SAP. Use key parameter for single entity, omit for collection. Supports $select and $expand.
199
+ - You don't know which service to query (use sap://routing/{domain} first)`,inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),entitySet:z$1.string().describe("Entity set to query"),filters:z$1.array(z$1.object({field:z$1.string().describe("Property name from entity schema"),operator:z$1.enum(["eq","ne","gt","ge","lt","le","contains","startswith","endswith"]).describe("Filter operator"),value:z$1.union([z$1.string(),z$1.number(),z$1.boolean()]).describe("Filter value")})).describe("Structured filters from NL interpretation"),select:z$1.array(z$1.string()).optional().describe("Properties to return"),orderby:z$1.string().optional().describe("Sort expression"),top:z$1.number().optional().describe("Maximum number of results"),skip:z$1.number().optional().describe("Number of results to skip")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:c,entitySet:l,filters:u,select:d,orderby:m,top:p,skip:f})=>{if(!W(l,n))return a(new Error(`Entity set '${l}' is not exposed. Configure --expose to include it.`));try{let g=o(c),h=(await g.getMetadata()).version,S=yr(u,h),v=await g.query(l,{filter:S||void 0,select:d,orderby:m,top:p,skip:f});return {content:[{type:"text",text:JSON.stringify({version:h,generatedFilter:S||"(none)",results:v.results,count:v.count,nextLink:v.nextLink},null,2)}]}}catch(g){return a(g)}});}function vr(t,e,n,r){let s=r??new E,i=new Map;function o(l){let u=Ee(l),d=i.get(u);return d||(d=new O(e,u,s),i.set(u,d)),d}function a(l,u){return P(l,{serviceUrl:u})}function c(l){return W(l,n)?null:a(new Error(`Entity set '${l}' is not exposed. Configure --expose to include it.`))}t.registerTool("sap_read",{title:"Read SAP Entity",description:`Read a single entity or entity collection from SAP. Use key parameter for single entity, omit for collection. Supports $select and $expand.
200
200
 
201
201
  **When to use:**
202
202
  - You have a specific entity key and want one record
@@ -214,7 +214,7 @@ Each entity set carries what SAP allows on it \u2014 \`creatable\`, \`updatable\
214
214
  **When NOT to use:**
215
215
  - You have a natural language question (use sap_nl_query instead)
216
216
  - You don't know which service to query (use sap://routing/{domain} first, then sap_discover_services)
217
- - You need to create, update, or delete data (use sap_create/sap_update/sap_delete)`,inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),entitySet:z$1.string().describe("Entity set name"),filter:z$1.string().optional().describe(`OData $filter expression, e.g., "Country eq 'DE'"`),select:z$1.array(z$1.string()).optional().describe("Properties to return"),expand:z$1.array(z$1.string()).optional().describe("Navigation properties to expand"),orderby:z$1.string().optional().describe('Sort expression, e.g., "CreationDate desc"'),top:z$1.number().optional().describe(`Maximum number of results (default ${50}, hard cap ${1e3}). Omitting it does NOT mean "all" \u2014 use skip plus the nextSkip value from the response to page through.`),skip:z$1.number().optional().describe("Number of results to skip"),maxPages:z$1.number().optional().describe("Maximum pagination pages to follow (default: 10)")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:l,entitySet:u,filter:d,select:m,expand:p,orderby:f,top:g,skip:y,maxPages:h})=>{let S=c(u);if(S)return S;try{let v=o(l),k=pe(g,y),L=await v.query(u,{filter:d,select:m,expand:p,orderby:f,top:k.top,skip:k.skip,maxPages:h}),F=me({returned:L.results.length,totalCount:L.count,paging:k,hasMoreHint:L.nextLink?!0:void 0});return {content:[{type:"text",text:JSON.stringify({...L,...F},null,2)}]}}catch(v){return a(v,l)}}),t.registerTool("sap_create",{title:"Create SAP Entity",description:"Create a new entity in SAP. Pass entity data as a JSON string in the data parameter. Use sap_get_metadata with entityType first to discover required fields.",inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),entitySet:z$1.string().describe("Entity set name"),data:z$1.string().describe(`Entity data as JSON string, e.g. '{"BusinessPartnerCategory": "2", "BusinessPartnerName": "Acme Corp", "Country": "DE"}'`)}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:false,openWorldHint:true}},async({serviceUrl:l,entitySet:u,data:d})=>{let m;try{m=JSON.parse(d);}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 p=c(u);if(p)return p;try{let g=await o(l).create(u,m);return {content:[{type:"text",text:JSON.stringify({created:g},null,2)}]}}catch(f){return a(f,l)}}),t.registerTool("sap_update",{title:"Update SAP Entity",description:"Update an existing SAP entity. Provide the entity key and the fields to update as JSON strings. ETag handling is automatic.",inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),entitySet:z$1.string().describe("Entity set name"),key:z$1.string().describe(`Entity key as JSON string, e.g. '{"BusinessPartner": "1000001"}'`),data:z$1.string().describe(`Fields to update as JSON string, e.g. '{"BusinessPartnerName": "New Name"}'`)}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:l,entitySet:u,key:d,data:m})=>{let p,f;try{p=JSON.parse(d),f=JSON.parse(m);}catch{return a(new Error("Invalid JSON in key or data parameter"))}let g=c(u);if(g)return g;try{return await o(l).update(u,p,f),{content:[{type:"text",text:JSON.stringify({updated:!0,entitySet:u,key:d})}]}}catch(y){return a(y,l)}}),t.registerTool("sap_delete",{title:"Delete SAP Entity",description:"Delete an entity from SAP. Provide the entity key. ETag handling is automatic.",inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),entitySet:z$1.string().describe("Entity set name"),key:z$1.string().describe(`Entity key as JSON string, e.g. '{"BusinessPartner": "1000001"}'`)}),annotations:{readOnlyHint:false,destructiveHint:true,idempotentHint:true,openWorldHint:true}},async({serviceUrl:l,entitySet:u,key:d})=>{let m;try{m=JSON.parse(d);}catch{return a(new Error("Invalid JSON in key parameter"))}let p=c(u);if(p)return p;try{return await o(l).delete(u,m),{content:[{type:"text",text:JSON.stringify({deleted:!0,entitySet:u,key:d})}]}}catch(f){return a(f,l)}});}function Oe(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=>Mo(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 Mo(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:$(t.MESSAGE_V1),messageV2:$(t.MESSAGE_V2),messageV3:$(t.MESSAGE_V3),messageV4:$(t.MESSAGE_V4),parameter:$(t.PARAMETER),row:Uo(t.ROW),field:$(t.FIELD),system:$(t.SYSTEM),logNo:$(t.LOG_NO),logMsgNo:$(t.LOG_MSG_NO)}}function $(t){if(t==null)return;let e=String(t);return e===""?void 0:e}function Uo(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 vr(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 dt=null;async function Tr(t){if(dt)return true;try{return dt=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 br(){if(!dt)throw new Error("node-rfc not loaded \u2014 call tryLoadRfc() first");return dt}var wr=6e4,Lo=5e3,Fo=2,jo=10,ge=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";}},ut=class{pool;sessions=new Map;logger;closed=false;constructor(e,n){let s=br().Pool;this.pool=new s({connectionParameters:{...e},poolOptions:{low:Fo,high:jo}}),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 ge(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 ge(e);clearTimeout(r.idleTimer);try{let s=n?{WAIT:"X"}:{},i=await r.client.call("BAPI_TRANSACTION_COMMIT",s),{hasErrors:o}=Oe(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()+Lo;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),wr)};return this.sessions.set(n,r),r}refreshIdle(e){clearTimeout(e.idleTimer),e.idleTimer=setTimeout(()=>this.onIdle(e.id),wr);}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 Ne(t){if(t instanceof ge)return {content:[{type:"text",text:JSON.stringify({code:"SESSION_NOT_FOUND",message:t.message,sessionId:t.sessionId})}],isError:true};let e=vr(t);return {content:[{type:"text",text:JSON.stringify(e)}],isError:true}}var Ho={I:"IMPORT",E:"EXPORT",C:"CHANGING",T:"TABLES",X:"EXCEPTION"};function Ar(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.
217
+ - You need to create, update, or delete data (use sap_create/sap_update/sap_delete)`,inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),entitySet:z$1.string().describe("Entity set name"),filter:z$1.string().optional().describe(`OData $filter expression, e.g., "Country eq 'DE'"`),select:z$1.array(z$1.string()).optional().describe("Properties to return"),expand:z$1.array(z$1.string()).optional().describe("Navigation properties to expand"),orderby:z$1.string().optional().describe('Sort expression, e.g., "CreationDate desc"'),top:z$1.number().optional().describe(`Maximum number of results (default ${50}, hard cap ${1e3}). Omitting it does NOT mean "all" \u2014 use skip plus the nextSkip value from the response to page through.`),skip:z$1.number().optional().describe("Number of results to skip"),maxPages:z$1.number().optional().describe("Maximum pagination pages to follow (default: 10)")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:l,entitySet:u,filter:d,select:m,expand:p,orderby:f,top:g,skip:y,maxPages:h})=>{let S=c(u);if(S)return S;try{let v=o(l),k=pe(g,y),L=await v.query(u,{filter:d,select:m,expand:p,orderby:f,top:k.top,skip:k.skip,maxPages:h}),F=me({returned:L.results.length,totalCount:L.count,paging:k,hasMoreHint:L.nextLink?!0:void 0});return {content:[{type:"text",text:JSON.stringify({...L,...F},null,2)}]}}catch(v){return a(v,l)}}),t.registerTool("sap_create",{title:"Create SAP Entity",description:"Create a new entity in SAP. Pass entity data as a JSON string in the data parameter. Use sap_get_metadata with entityType first to discover required fields.",inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),entitySet:z$1.string().describe("Entity set name"),data:z$1.string().describe(`Entity data as JSON string, e.g. '{"BusinessPartnerCategory": "2", "BusinessPartnerName": "Acme Corp", "Country": "DE"}'`)}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:false,openWorldHint:true}},async({serviceUrl:l,entitySet:u,data:d})=>{let m;try{m=JSON.parse(d);}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 p=c(u);if(p)return p;try{let g=await o(l).create(u,m);return {content:[{type:"text",text:JSON.stringify({created:g},null,2)}]}}catch(f){return a(f,l)}}),t.registerTool("sap_update",{title:"Update SAP Entity",description:"Update an existing SAP entity. Provide the entity key and the fields to update as JSON strings. ETag handling is automatic.",inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),entitySet:z$1.string().describe("Entity set name"),key:z$1.string().describe(`Entity key as JSON string, e.g. '{"BusinessPartner": "1000001"}'`),data:z$1.string().describe(`Fields to update as JSON string, e.g. '{"BusinessPartnerName": "New Name"}'`)}),annotations:{readOnlyHint:false,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({serviceUrl:l,entitySet:u,key:d,data:m})=>{let p,f;try{p=JSON.parse(d),f=JSON.parse(m);}catch{return a(new Error("Invalid JSON in key or data parameter"))}let g=c(u);if(g)return g;try{return await o(l).update(u,p,f),{content:[{type:"text",text:JSON.stringify({updated:!0,entitySet:u,key:d})}]}}catch(y){return a(y,l)}}),t.registerTool("sap_delete",{title:"Delete SAP Entity",description:"Delete an entity from SAP. Provide the entity key. ETag handling is automatic.",inputSchema:z$1.object({serviceUrl:z$1.string().describe("OData service URL"),entitySet:z$1.string().describe("Entity set name"),key:z$1.string().describe(`Entity key as JSON string, e.g. '{"BusinessPartner": "1000001"}'`)}),annotations:{readOnlyHint:false,destructiveHint:true,idempotentHint:true,openWorldHint:true}},async({serviceUrl:l,entitySet:u,key:d})=>{let m;try{m=JSON.parse(d);}catch{return a(new Error("Invalid JSON in key parameter"))}let p=c(u);if(p)return p;try{return await o(l).delete(u,m),{content:[{type:"text",text:JSON.stringify({deleted:!0,entitySet:u,key:d})}]}}catch(f){return a(f,l)}});}function Oe(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=>Mo(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 Mo(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:$(t.MESSAGE_V1),messageV2:$(t.MESSAGE_V2),messageV3:$(t.MESSAGE_V3),messageV4:$(t.MESSAGE_V4),parameter:$(t.PARAMETER),row:Uo(t.ROW),field:$(t.FIELD),system:$(t.SYSTEM),logNo:$(t.LOG_NO),logMsgNo:$(t.LOG_MSG_NO)}}function $(t){if(t==null)return;let e=String(t);return e===""?void 0:e}function Uo(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 br(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 dt=null;async function Tr(t){if(dt)return true;try{return dt=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 wr(){if(!dt)throw new Error("node-rfc not loaded \u2014 call tryLoadRfc() first");return dt}var Ar=6e4,Lo=5e3,Fo=2,jo=10,ge=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";}},ut=class{pool;sessions=new Map;logger;closed=false;constructor(e,n){let s=wr().Pool;this.pool=new s({connectionParameters:{...e},poolOptions:{low:Fo,high:jo}}),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 ge(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 ge(e);clearTimeout(r.idleTimer);try{let s=n?{WAIT:"X"}:{},i=await r.client.call("BAPI_TRANSACTION_COMMIT",s),{hasErrors:o}=Oe(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()+Lo;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),Ar)};return this.sessions.set(n,r),r}refreshIdle(e){clearTimeout(e.idleTimer),e.idleTimer=setTimeout(()=>this.onIdle(e.id),Ar);}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 Ne(t){if(t instanceof ge)return {content:[{type:"text",text:JSON.stringify({code:"SESSION_NOT_FOUND",message:t.message,sessionId:t.sessionId})}],isError:true};let e=br(t);return {content:[{type:"text",text:JSON.stringify(e)}],isError:true}}var Ho={I:"IMPORT",E:"EXPORT",C:"CHANGING",T:"TABLES",X:"EXCEPTION"};function Cr(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.
218
218
 
219
219
  When to use:
220
220
  - Custom Z_* function modules (no BAPI commit needed)
@@ -233,7 +233,7 @@ When to use:
233
233
  When NOT to use:
234
234
  - To search for function modules by name pattern -> use sap_rfc_search_functions
235
235
 
236
- Returns a structured FunctionInterface object: name, remoteEnabled, updateTask, parameters[].`,inputSchema:z$1.object({functionModule:z$1.string().min(1).describe("Function module name, e.g. BAPI_USER_GET_DETAIL"),language:z$1.string().length(1).optional().describe("Single-character language key for parameter texts (default: server config)")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({functionModule:r,language:s})=>{try{let i=r.toUpperCase(),o={FUNCNAME:i};s&&(o.LANGUAGE=s);let a=await e.callOnce("RFC_GET_FUNCTION_INTERFACE",o),l=(a.PARAMS??[]).map(d=>{let m=String(d.PARAMCLASS??"").trim(),p=Ho[m]??"IMPORT",f=String(d.TABNAME??"").trim(),g=String(d.FIELDNAME??"").trim(),y=f&&g?`${f}-${g}`:f||g||void 0,h=String(d.DECIMALS??"").trim(),S=h===""?void 0:Number.parseInt(h,10),v=Number.isNaN(S)?void 0:S,k=String(d.INTLENGTH??"0").trim(),L=Number.parseInt(k===""?"0":k,10)||0,F=String(d.DEFAULT??"").trim(),X=String(d.PARAMTEXT??"").trim();return {name:String(d.PARAMETER??"").trim(),direction:p,type:String(d.EXID??"").trim(),length:L,decimals:v,optional:String(d.OPTIONAL??"").trim()==="X",default:F===""?void 0:F,text:X===""?void 0:X,ddicReference:y}}),u={name:i,remoteEnabled:String(a.REMOTE_CALL??"")==="X",updateTask:String(a.UPDATE_TASK??"")==="X",parameters:l};return {content:[{type:"text",text:JSON.stringify(u,null,2)}]}}catch(i){return Ne(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.
236
+ Returns a structured FunctionInterface object: name, remoteEnabled, updateTask, parameters[].`,inputSchema:z$1.object({functionModule:z$1.string().min(1).describe("Function module name, e.g. BAPI_USER_GET_DETAIL"),language:z$1.string().length(1).optional().describe("Single-character language key for parameter texts (default: server config)")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({functionModule:r,language:s})=>{try{let i=r.toUpperCase(),o={FUNCNAME:i};s&&(o.LANGUAGE=s);let a=await e.callOnce("RFC_GET_FUNCTION_INTERFACE",o),l=(a.PARAMS??[]).map(d=>{let m=String(d.PARAMCLASS??"").trim(),p=Ho[m]??"IMPORT",f=String(d.TABNAME??"").trim(),g=String(d.FIELDNAME??"").trim(),y=f&&g?`${f}-${g}`:f||g||void 0,h=String(d.DECIMALS??"").trim(),S=h===""?void 0:Number.parseInt(h,10),v=Number.isNaN(S)?void 0:S,k=String(d.INTLENGTH??"0").trim(),L=Number.parseInt(k===""?"0":k,10)||0,F=String(d.DEFAULT??"").trim(),Q=String(d.PARAMTEXT??"").trim();return {name:String(d.PARAMETER??"").trim(),direction:p,type:String(d.EXID??"").trim(),length:L,decimals:v,optional:String(d.OPTIONAL??"").trim()==="X",default:F===""?void 0:F,text:Q===""?void 0:Q,ddicReference:y}}),u={name:i,remoteEnabled:String(a.REMOTE_CALL??"")==="X",updateTask:String(a.UPDATE_TASK??"")==="X",parameters:l};return {content:[{type:"text",text:JSON.stringify(u,null,2)}]}}catch(i){return Ne(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.
237
237
 
238
238
  When to use:
239
239
  - BAPI_USER_CREATE1, BAPI_SALESORDER_CREATEFROMDAT2, BAPI_*_CHANGE, etc.
@@ -266,7 +266,7 @@ When NOT to use:
266
266
  Wildcards (SAP-native, NOT glob):
267
267
  - \`*\` matches any sequence of characters
268
268
  - \`+\` matches a single character
269
- 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$1.object({pattern:z$1.string().min(1).describe('Name pattern with SAP wildcards (* and +). Examples: "BAPI_USER_*", "Z_*", "+MATERIAL+"'),groupName:z$1.string().optional().describe('Optional function group filter (RS38L_AREA, e.g. "SU05")'),limit:z$1.number().int().positive().optional().default(100).describe("Max number of hits to return (default 100). Excess is truncated.")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({pattern:r,groupName:s,limit:i})=>{try{let o={FUNCNAME:r.toUpperCase()};s&&(o.GROUPNAME=s.toUpperCase());let c=(await e.callOnce("RFC_FUNCTION_SEARCH",o)).FUNCTIONS??[],l=c.length,u=i??100,d=l>u,f={functions:(d?c.slice(0,u):c).map(g=>{let y=String(g.TEXT??"").trim();return {name:String(g.FUNCNAME??"").trim(),groupName:String(g.GROUPNAME??"").trim(),shortText:y===""?void 0:y}}),total:l,...d?{truncated:!0}:{}};return {content:[{type:"text",text:JSON.stringify(f,null,2)}]}}catch(o){return Ne(o)}});}function De(t,e){let n=new McpServer({name:Pn,version:Rn},{capabilities:{logging:{},resources:{},prompts:{},tools:{listChanged:true}}}),r={readOnly:e?.readOnly??false,activeTiers:e?.tiers??Jn,...e?.allowedTiers?{allowedTiers:e.allowedTiers}:{},hasIdocConfig:e?.hasIdocConfig??false,hasRfcConfig:e?.hasRfcConfig??false},s=new at(r),i=n.registerTool.bind(n),o=e?.toolTimeoutMs??et*1e3;n.registerTool=((y,...h)=>{let S=h.length-1,v=h[S];typeof v=="function"&&(h[S]=Cn(y,v,{toolTimeoutMs:o,logger:e?.logger,tlsVerifyDisabled:e?.httpOptions?.tlsVerify===false}));let k=i(y,...h);return s.registerHandle(y,k),k});let a={baseUrl:t.baseUrl,...e?.cacheDir?{persistDir:e.cacheDir}:{},...e?.logger?{onWarn:(y,h)=>e.logger?.warn({err:h},y)}:{}},c=e?.caches?.catalog??new H(a),l=e?.caches?.metadata??new E(a),u=Me(t),d=new re(u,e?.httpOptions);e?.userContext&&(d.userContext=e.userContext);let m=e?.exposePatterns,p=e?.sapClient??t.sapClient;In(n),kn(n),dr(n,d,{baseUrl:t.baseUrl,sapClient:p,tlsVerify:e?.httpOptions?.tlsVerify}),cr(n,d,m),ar(n,d,{catalogV2Url:e?.catalogV2Url,catalogV4Url:e?.catalogV4Url,baseUrl:t.baseUrl,onDiscoveryMatch:e?.onDiscoveryMatch,...e?.concurrency?{concurrency:e.concurrency}:{}},c),Sr(n,d,m,l),lr(n,d,l),Zn(n,d,l),yr(n,d,m,l);let f=e?.idocStore??new de,g=new Ze(d,p,e?.partnerDefaults);return mr(n,d,g,f),Wn(n,f),e?.rfcAvailable&&e.rfcPool&&e.logger&&Ar(n,e.rfcPool,e.logger),gr(n,s),zn(n,d,l),qn(n,d,l),Gn(n),s.applyInitialVisibility(),{server:n,registry:s,httpClient:d}}var Vo=z.object({snc_qop:z.enum(["1","2","3","8","9"]).optional(),snc_myname:z.string().min(1).optional(),snc_partnername:z.string().min(1).optional(),snc_lib:z.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 Ht(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=Vo.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 Cr(t=process.env){try{return Ht(t)!==null}catch{return false}}var pt=class{constructor(e={}){this.options=e;this.discovery=e.discoveryClient??new K;}discovery;jwks;jwksIssuer;get verifies(){return !!this.options.issuerUrl}async verify(e){let n=e.authorization??e.Authorization;if(typeof n!="string"||!n.startsWith("Bearer "))return {kind:"none"};let r=n.slice(7).trim();if(!r)return {kind:"none"};if(!this.options.issuerUrl)return this.withoutVerification(r);try{let s=await this.getKeySet(this.options.issuerUrl),{payload:i}=await jwtVerify(r,s,{issuer:void 0,...this.options.audience?{audience:this.options.audience}:{}});return typeof i.sub!="string"?{kind:"rejected",reason:"Token ohne sub-Claim"}:{kind:"user",context:{token:r,sub:i.sub,email:typeof i.email=="string"?i.email:void 0,name:typeof i.name=="string"?i.name:void 0,verified:!0}}}catch(s){return Ko(r)?{kind:"rejected",reason:s instanceof Error?s.message:"Signaturpr\xFCfung fehlgeschlagen"}:{kind:"none"}}}withoutVerification(e){try{let n=decodeJwt(e);return typeof n.sub!="string"?{kind:"none"}:{kind:"user",context:{token:e,sub:n.sub,email:typeof n.email=="string"?n.email:void 0,name:typeof n.name=="string"?n.name:void 0,verified:!1}}}catch{return {kind:"none"}}}async getKeySet(e){if(this.jwks&&this.jwksIssuer===e)return this.jwks;let n=await this.discovery.discover(e);return this.jwks=createRemoteJWKSet(new URL(n.jwks_uri)),this.jwksIssuer=e,this.jwks}};function Ko(t){let e=t.split(".");return e.length===3&&e.every(n=>n.length>0)}function _r(t,e){return e?.kind==="token"?t.destinations?.get(e.token.destination):t.destinations?t.destinations.defaultDestination():t.config}function Er(t,e){if(e?.kind!=="token"||!e.token.policy)return t;let n=e.token.policy;return {...t,readOnly:t.readOnly===true||n.readOnly===true,...n.tiers?{allowedTiers:[...n.tiers]}:{}}}function Rr(t){t.writeHead(503,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Keine Destination bestimmbar",hint:'Es sind mehrere Destinations konfiguriert, aber keine hei\xDFt "default". Benenne eine Destination "default" oder reduziere die Datei auf ein Ziel.'}));}var Bt=class{constructor(e){this.persistDir=e;}entries=new Map;for(e){let n=`${e.name}\0${e.baseUrl}`,r=this.entries.get(n);if(!r){for(let i of this.entries.keys())i.startsWith(`${e.name}\0`)&&this.entries.delete(i);let s={baseUrl:e.baseUrl,...this.persistDir?{persistDir:this.persistDir}:{}};r={catalog:new H(s),metadata:new E(s)},this.entries.set(n,r);}return r}};async function Pr(t){let e=new pt({...t.jwtIssuer?{issuerUrl:t.jwtIssuer}:{},...t.jwtAudience?{audience:t.jwtAudience}:{}}),n=createMcpExpressApp({host:"0.0.0.0"}),r=new We({apiKey:t.apiKey,tokens:()=>t.destinations?.tokens()??[]}),s=t.serverOptions?.logger;n.use((a,c,l)=>{let u=a,d=c,m=l;if(u.path==="/healthz"){m();return}if(t.webhookSecret&&u.path==="/idoc"){m();return}let p=u.ip??u.socket?.remoteAddress??"unknown",f=r.authenticate(u.headers.authorization,p);if(f.kind==="rate-limited"){s?.warn({clientAddress:p,path:u.path,retryAfterSeconds:f.retryAfterSeconds},"MCP-Anfrage abgewiesen: zu viele fehlgeschlagene Anmeldeversuche von dieser Adresse"),d.setHeader("Retry-After",String(f.retryAfterSeconds)),d.status(429).json({error:"Too Many Requests",retryAfterSeconds:f.retryAfterSeconds});return}if(f.kind==="rejected"){s?.warn({clientAddress:p,path:u.path,reason:f.reason},"MCP-Anfrage abgewiesen"),d.status(401).json({error:"Unauthorized"});return}u.sapAuth=f,m();}),n.get("/healthz",(a,c)=>{c.status(200).json({status:"ok"});}),t.transport==="http"?Qo(n,t,e):Zo(n,t,e);let i=Je(t.idocStore,{secret:t.webhookSecret,maxBodyBytes:t.webhookMaxBodyBytes});n.post("/idoc",(a,c)=>{i(a,c);});let o=createServer(n);return new Promise(a=>{o.listen(t.port,()=>{let c=o.address(),l=typeof c=="object"&&c!==null?c.port:t.port;a({port:l,close:()=>new Promise((u,d)=>{o.close(m=>m?d(m):u());})});});})}function Qo(t,e,n){let r=new Bt(e.serverOptions?.cacheDir);t.all("/mcp",(s,i)=>{let o=s,a=i,c=_r(e,o.sapAuth);if(!c){Rr(a);return}let l=new StreamableHTTPServerTransport({sessionIdGenerator:void 0}),u=d=>{let{server:m}=De(c,{...Er(e.serverOptions,o.sapAuth),sapClient:c.sapClient,userContext:d,caches:r.for(c)});m.connect(l).then(()=>l.handleRequest(o,a,o.body)).catch(()=>{a.headersSent||(a.writeHead(500),a.end("Internal Server Error"));});};n.verify(o.headers).then(d=>{if(d.kind==="rejected"){a.writeHead(401,{"Content-Type":"application/json"}),a.end(JSON.stringify({error:"Unauthorized",reason:d.reason,hint:"Das mitgeschickte Bearer-Token hat die Signaturpr\xFCfung gegen SAP_JWT_ISSUER nicht bestanden."}));return}let m=d.kind==="user"?d.context:null;u(m);});});}function Zo(t,e,n){let r=new Map;t.get("/sse",(s,i)=>{let o=s,a=i,c=_r(e,o.sapAuth);if(!c){Rr(a);return}let l=new SSEServerTransport("/message",a),{server:u,httpClient:d}=De(c,{...Er(e.serverOptions,o.sapAuth),sapClient:c.sapClient});u.connect(l).then(()=>l.start()).then(()=>{r.set(l.sessionId,{transport:l,server:u,httpClient:d}),l.onclose=()=>{r.delete(l.sessionId);};}).catch(()=>{a.headersSent||(a.writeHead(500),a.end("Internal Server Error"));});}),t.post("/message",(s,i)=>{let o=s,a=i,l=new URL(o.url??"",`http://${o.headers.host??"localhost"}`).searchParams.get("sessionId")??o.query?.sessionId;if(!l||!r.has(l)){a.writeHead(400,{"Content-Type":"application/json"}),a.end(JSON.stringify({error:"Invalid or missing session ID"}));return}let u=r.get(l);n.verify(o.headers).then(d=>{if(d.kind==="rejected"){a.writeHead(401,{"Content-Type":"application/json"}),a.end(JSON.stringify({error:"Unauthorized",reason:d.reason}));return}u.httpClient.userContext=d.kind==="user"?d.context:null,u.transport.handlePostMessage(o,a,o.body).catch(()=>{a.headersSent||(a.writeHead(500),a.end("Internal Server Error"));});});});}var ra=`Usage: guniweb-sap-mcp [options]
269
+ 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$1.object({pattern:z$1.string().min(1).describe('Name pattern with SAP wildcards (* and +). Examples: "BAPI_USER_*", "Z_*", "+MATERIAL+"'),groupName:z$1.string().optional().describe('Optional function group filter (RS38L_AREA, e.g. "SU05")'),limit:z$1.number().int().positive().optional().default(100).describe("Max number of hits to return (default 100). Excess is truncated.")}),annotations:{readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:true}},async({pattern:r,groupName:s,limit:i})=>{try{let o={FUNCNAME:r.toUpperCase()};s&&(o.GROUPNAME=s.toUpperCase());let c=(await e.callOnce("RFC_FUNCTION_SEARCH",o)).FUNCTIONS??[],l=c.length,u=i??100,d=l>u,f={functions:(d?c.slice(0,u):c).map(g=>{let y=String(g.TEXT??"").trim();return {name:String(g.FUNCNAME??"").trim(),groupName:String(g.GROUPNAME??"").trim(),shortText:y===""?void 0:y}}),total:l,...d?{truncated:!0}:{}};return {content:[{type:"text",text:JSON.stringify(f,null,2)}]}}catch(o){return Ne(o)}});}function De(t,e){let n=new McpServer({name:kn,version:Pn},{capabilities:{logging:{},resources:{},prompts:{},tools:{listChanged:true}}}),r={readOnly:e?.readOnly??false,activeTiers:e?.tiers??Xn,...e?.allowedTiers?{allowedTiers:e.allowedTiers}:{},hasIdocConfig:e?.hasIdocConfig??false,hasRfcConfig:e?.hasRfcConfig??false},s=new at(r),i=n.registerTool.bind(n),o=e?.toolTimeoutMs??et*1e3;n.registerTool=((y,...h)=>{let S=h.length-1,v=h[S];typeof v=="function"&&(h[S]=_n(y,v,{toolTimeoutMs:o,logger:e?.logger,tlsVerifyDisabled:e?.httpOptions?.tlsVerify===false}));let k=i(y,...h);return s.registerHandle(y,k),k});let a={baseUrl:t.baseUrl,...e?.cacheDir?{persistDir:e.cacheDir}:{},...e?.logger?{onWarn:(y,h)=>e.logger?.warn({err:h},y)}:{}},c=e?.caches?.catalog??new H(a),l=e?.caches?.metadata??new E(a),u=Me(t),d=new se(u,e?.httpOptions);e?.userContext&&(d.userContext=e.userContext);let m=e?.exposePatterns,p=e?.sapClient??t.sapClient;On(n),In(n),ur(n,d,{baseUrl:t.baseUrl,sapClient:p,tlsVerify:e?.httpOptions?.tlsVerify}),lr(n,d,m),cr(n,d,{catalogV2Url:e?.catalogV2Url,catalogV4Url:e?.catalogV4Url,baseUrl:t.baseUrl,onDiscoveryMatch:e?.onDiscoveryMatch,...e?.concurrency?{concurrency:e.concurrency}:{}},c),vr(n,d,m,l),dr(n,d,l),er(n,d,l),Sr(n,d,m,l);let f=e?.idocStore??new de,g=new Ze(d,p,e?.partnerDefaults);return fr(n,d,g,f),zn(n,f),e?.rfcAvailable&&e.rfcPool&&e.logger&&Cr(n,e.rfcPool,e.logger),hr(n,s),Kn(n,d,l),Wn(n,d,l),Yn(n),s.applyInitialVisibility(),{server:n,registry:s,httpClient:d}}var Vo=z.object({snc_qop:z.enum(["1","2","3","8","9"]).optional(),snc_myname:z.string().min(1).optional(),snc_partnername:z.string().min(1).optional(),snc_lib:z.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 Ht(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=Vo.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 _r(t=process.env){try{return Ht(t)!==null}catch{return false}}var pt=class{constructor(e={}){this.options=e;this.discovery=e.discoveryClient??new K;}discovery;jwks;jwksIssuer;get verifies(){return !!this.options.issuerUrl}async verify(e){let n=e.authorization??e.Authorization;if(typeof n!="string"||!n.startsWith("Bearer "))return {kind:"none"};let r=n.slice(7).trim();if(!r)return {kind:"none"};if(!this.options.issuerUrl)return this.withoutVerification(r);try{let s=await this.getKeySet(this.options.issuerUrl),{payload:i}=await jwtVerify(r,s,{issuer:void 0,...this.options.audience?{audience:this.options.audience}:{}});return typeof i.sub!="string"?{kind:"rejected",reason:"Token ohne sub-Claim"}:{kind:"user",context:{token:r,sub:i.sub,email:typeof i.email=="string"?i.email:void 0,name:typeof i.name=="string"?i.name:void 0,verified:!0}}}catch(s){return Ko(r)?{kind:"rejected",reason:s instanceof Error?s.message:"Signaturpr\xFCfung fehlgeschlagen"}:{kind:"none"}}}withoutVerification(e){try{let n=decodeJwt(e);return typeof n.sub!="string"?{kind:"none"}:{kind:"user",context:{token:e,sub:n.sub,email:typeof n.email=="string"?n.email:void 0,name:typeof n.name=="string"?n.name:void 0,verified:!1}}}catch{return {kind:"none"}}}async getKeySet(e){if(this.jwks&&this.jwksIssuer===e)return this.jwks;let n=await this.discovery.discover(e);return this.jwks=createRemoteJWKSet(new URL(n.jwks_uri)),this.jwksIssuer=e,this.jwks}};function Ko(t){let e=t.split(".");return e.length===3&&e.every(n=>n.length>0)}function Er(t,e){return e?.kind==="token"?t.destinations?.get(e.token.destination):t.destinations?t.destinations.defaultDestination():t.config}function Rr(t,e){if(e?.kind!=="token"||!e.token.policy)return t;let n=e.token.policy;return {...t,readOnly:t.readOnly===true||n.readOnly===true,...n.tiers?{allowedTiers:[...n.tiers]}:{}}}function Pr(t){t.writeHead(503,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Keine Destination bestimmbar",hint:'Es sind mehrere Destinations konfiguriert, aber keine hei\xDFt "default". Benenne eine Destination "default" oder reduziere die Datei auf ein Ziel.'}));}var Bt=class{constructor(e){this.persistDir=e;}entries=new Map;for(e){let n=`${e.name}\0${e.baseUrl}`,r=this.entries.get(n);if(!r){for(let i of this.entries.keys())i.startsWith(`${e.name}\0`)&&this.entries.delete(i);let s={baseUrl:e.baseUrl,...this.persistDir?{persistDir:this.persistDir}:{}};r={catalog:new H(s),metadata:new E(s)},this.entries.set(n,r);}return r}};async function kr(t){let e=new pt({...t.jwtIssuer?{issuerUrl:t.jwtIssuer}:{},...t.jwtAudience?{audience:t.jwtAudience}:{}}),n=createMcpExpressApp({host:"0.0.0.0"}),r=new We({apiKey:t.apiKey,tokens:()=>t.destinations?.tokens()??[]}),s=t.serverOptions?.logger;n.use((a,c,l)=>{let u=a,d=c,m=l;if(u.path==="/healthz"){m();return}if(t.webhookSecret&&u.path==="/idoc"){m();return}let p=u.ip??u.socket?.remoteAddress??"unknown",f=r.authenticate(u.headers.authorization,p);if(f.kind==="rate-limited"){s?.warn({clientAddress:p,path:u.path,retryAfterSeconds:f.retryAfterSeconds},"MCP-Anfrage abgewiesen: zu viele fehlgeschlagene Anmeldeversuche von dieser Adresse"),d.setHeader("Retry-After",String(f.retryAfterSeconds)),d.status(429).json({error:"Too Many Requests",retryAfterSeconds:f.retryAfterSeconds});return}if(f.kind==="rejected"){s?.warn({clientAddress:p,path:u.path,reason:f.reason},"MCP-Anfrage abgewiesen"),d.status(401).json({error:"Unauthorized"});return}u.sapAuth=f,m();}),n.get("/healthz",(a,c)=>{c.status(200).json({status:"ok"});}),t.transport==="http"?Qo(n,t,e):Zo(n,t,e);let i=Je(t.idocStore,{secret:t.webhookSecret,maxBodyBytes:t.webhookMaxBodyBytes});n.post("/idoc",(a,c)=>{i(a,c);});let o=createServer(n);return new Promise(a=>{o.listen(t.port,()=>{let c=o.address(),l=typeof c=="object"&&c!==null?c.port:t.port;a({port:l,close:()=>new Promise((u,d)=>{o.close(m=>m?d(m):u());})});});})}function Qo(t,e,n){let r=new Bt(e.serverOptions?.cacheDir);t.all("/mcp",(s,i)=>{let o=s,a=i,c=Er(e,o.sapAuth);if(!c){Pr(a);return}let l=new StreamableHTTPServerTransport({sessionIdGenerator:void 0}),u=d=>{let{server:m}=De(c,{...Rr(e.serverOptions,o.sapAuth),sapClient:c.sapClient,userContext:d,caches:r.for(c)});m.connect(l).then(()=>l.handleRequest(o,a,o.body)).catch(()=>{a.headersSent||(a.writeHead(500),a.end("Internal Server Error"));});};n.verify(o.headers).then(d=>{if(d.kind==="rejected"){a.writeHead(401,{"Content-Type":"application/json"}),a.end(JSON.stringify({error:"Unauthorized",reason:d.reason,hint:"Das mitgeschickte Bearer-Token hat die Signaturpr\xFCfung gegen SAP_JWT_ISSUER nicht bestanden."}));return}let m=d.kind==="user"?d.context:null;u(m);});});}function Zo(t,e,n){let r=new Map;t.get("/sse",(s,i)=>{let o=s,a=i,c=Er(e,o.sapAuth);if(!c){Pr(a);return}let l=new SSEServerTransport("/message",a),{server:u,httpClient:d}=De(c,{...Rr(e.serverOptions,o.sapAuth),sapClient:c.sapClient});u.connect(l).then(()=>l.start()).then(()=>{r.set(l.sessionId,{transport:l,server:u,httpClient:d}),l.onclose=()=>{r.delete(l.sessionId);};}).catch(()=>{a.headersSent||(a.writeHead(500),a.end("Internal Server Error"));});}),t.post("/message",(s,i)=>{let o=s,a=i,l=new URL(o.url??"",`http://${o.headers.host??"localhost"}`).searchParams.get("sessionId")??o.query?.sessionId;if(!l||!r.has(l)){a.writeHead(400,{"Content-Type":"application/json"}),a.end(JSON.stringify({error:"Invalid or missing session ID"}));return}let u=r.get(l);n.verify(o.headers).then(d=>{if(d.kind==="rejected"){a.writeHead(401,{"Content-Type":"application/json"}),a.end(JSON.stringify({error:"Unauthorized",reason:d.reason}));return}u.httpClient.userContext=d.kind==="user"?d.context:null,u.transport.handlePostMessage(o,a,o.body).catch(()=>{a.headersSent||(a.writeHead(500),a.end("Internal Server Error"));});});});}var ra=`Usage: guniweb-sap-mcp [options]
270
270
  guniweb-sap-mcp destinations list|add|remove ... (manage destinations.json)
271
271
  guniweb-sap-mcp tokens issue|revoke|list ... (see: guniweb-sap-mcp tokens --help)
272
272
 
@@ -313,6 +313,7 @@ Configuration via environment variables: SAP_BASE_URL, SAP_AUTH_TYPE,
313
313
  SAP_WEBHOOK_SECRET, SAP_WEBHOOK_HOST, SAP_WEBHOOK_MAX_BODY_BYTES, etc.
314
314
  See README for the full list.
315
315
 
316
- Repository: https://github.com/jhaertf/guniweb-sap-mcp
317
- `;async function sa(){let t=process.argv.slice(2);if(t[0]!==void 0&&ln.has(t[0])){let h=await dn(t,{env:process.env,stdout:S=>process.stdout.write(S),stderr:S=>process.stderr.write(S)});process.exit(h);}let e=Vt();e.help&&(process.stdout.write(ra),process.exit(0));let n=[],r=e.destinationsFile?await we.fromFile(e.destinationsFile,{env:process.env,onWarn:h=>n.push(h)}):we.fromEnv(process.env),s=e.transport??"stdio",i=r.defaultDestination(),o=r.tokens().length;if(!i&&(s==="stdio"||o===0))throw new Error(`${r.filePath} definiert ${r.names().length} Destinations (${r.names().join(", ")}), aber keine hei\xDFt "default"${s==="stdio"?' \u2014 im stdio-Betrieb gibt es genau eine Verbindung, also muss eine Destination "default" hei\xDFen oder die Datei genau ein Ziel enthalten.':' und es sind keine Tokens konfiguriert, die eine Anfrage einem Ziel zuordnen k\xF6nnten. Benenne eine Destination "default" oder lege Tokens an.'}`);let a=Tt(process.env),c=us({transport:{target:"pino/file",options:{destination:2}}});a.tlsVerify===false&&c.warn({tlsVerify:false},"SAP_TLS_VERIFY=false: TLS certificate validation disabled. ONLY use for development/test against self-signed certs. Production deployments MUST use SAP_TLS_VERIFY=true (or unset)."),a.clientCert&&c.info({hasCert:true,hasPassphrase:!!a.clientKeyPassphrase},"X.509 client certificate enabled for OData transport"),c.info(i?{baseUrl:i.baseUrl,authType:i.authType}:{destinations:r.names(),tokens:o},"SAP MCP server starting");for(let h of n)c.warn(h);r.source==="file"?(c.info({file:r.filePath,destinations:r.names(),defaultDestination:i?.name??null,tokens:o},"Destinations aus Datei geladen"),r.onChange(h=>{c.info({version:h.version,destinations:h.names,tokens:h.tokenCount},"Destinations neu geladen");}),r.onError(h=>{c.warn({errors:h},"Destinations-Datei ung\xFCltig \u2014 die letzte g\xFCltige Konfiguration bleibt in Kraft");})):c.info({destinations:r.names()},'Destination "default" aus SAP_*-Umgebung (keine destinations.json konfiguriert)'),e.allowWriteExplicit?c.warn("Schreibwerkzeuge sind aktiv (--allow-write): sap_create, sap_update, sap_delete, sap_function und sap_idoc_send k\xF6nnen Daten im SAP-System ver\xE4ndern."):c.info("Nur-Lese-Betrieb (Standard seit 1.0). Schreibwerkzeuge sind nicht registriert; mit --allow-write bzw. SAP_MCP_ALLOW_WRITE=true aktivieren.");for(let h of r.names())Zt(r.names().length>1?c.child({destination:h}):c,r.get(h)?.sapClient);e.transport&&e.transport!=="stdio"&&(process.env.SAP_JWT_ISSUER?c.info({issuer:process.env.SAP_JWT_ISSUER},"Eingehende Benutzer-Token werden gegen die JWKS des Issuers gepr\xFCft"):c.warn("SAP_JWT_ISSUER ist nicht gesetzt: eingehende Bearer-Token werden nur dekodiert, nicht auf ihre Signatur gepr\xFCft. F\xFCr Principal Propagation an SAP ist das vertretbar (SAP pr\xFCft selbst); eine lokale Entscheidung darf sich auf die Identit\xE4t aber nicht st\xFCtzen.")),e.expose&&c.info({exposePatterns:e.expose},"Entity set filtering active");let l=await Tr(c),u;if(l){let h=Ht(process.env);if(h){h.snc_lib&&!existsSync(h.snc_lib)&&c.warn({path:h.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."),u=new ut(h,c);let S=!!h.snc_qop;c.info({ashost:h.ashost,mshost:h.mshost,snc:S,...S?{qop:h.snc_qop,myname:h.snc_myname}:{}},S?"RFC pool ready (SNC-secured)":"RFC pool ready");}else c.info("node-rfc loaded but no SAP_RFC_* config \u2014 RFC tier requires sap_enable_tools rfc + env vars to activate");}let d=new de,m={exposePatterns:e.expose,idocStore:d,sapClient:i?.sapClient,partnerDefaults:{sndpor:e.sndpor,sndprt:e.sndprt,sndprn:e.sndprn,rcvpor:e.rcvpor,rcvprt:e.rcvprt,rcvprn:e.rcvprn},catalogV2Url:e.catalogV2Url,catalogV4Url:e.catalogV4Url,concurrency:e.concurrency,httpOptions:a,onDiscoveryMatch:h=>{c.info({path:h,attemptedPaths:3},"V2 catalog auto-discovery: matched path");}},p=e.toolTimeoutSeconds??et;p===0?c.warn("Zeitbudget je Tool-Aufruf abgeschaltet (--tool-timeout 0): ein Aufruf kann unbegrenzt laufen und die Client-Verbindung bis zu deren eigenem Timeout blockieren."):c.info({toolTimeoutSeconds:p},"Zeitbudget je Tool-Aufruf aktiv"),e.cacheDir?c.info({cacheDir:e.cacheDir},"Cache-Ablage aktiv (\xFCberlebt Neustarts)"):c.info("Kein Cache-Verzeichnis konfiguriert (--cache-dir / SAP_CACHE_DIR): Katalog und Metadaten werden nach jedem Neustart erneut aus SAP geholt.");let f={...m,toolTimeoutMs:p*1e3,cacheDir:e.cacheDir,readOnly:e.readOnly,tiers:e.tiers,hasIdocConfig:qt(e),hasRfcConfig:Cr(process.env),rfcAvailable:l,rfcPool:u,logger:c},g;if(s==="stdio"){if(!i)throw new Error("stdio-Betrieb ohne Standard-Destination.");let{server:h}=De(i,f),S=new StdioServerTransport;if(await h.connect(S),c.info("SAP MCP server connected on stdio transport"),e.webhookPort){e.webhookSecret||c.warn({port:e.webhookPort},"IDoc webhook has NO authentication: anyone reaching this port can inject IDocs. Set --webhook-secret (or SAP_WEBHOOK_SECRET) for production.");let v=await gn(e.webhookPort,d,{host:e.webhookHost,secret:e.webhookSecret,maxBodyBytes:e.webhookMaxBodyBytes});c.info({port:v.port,auth:e.webhookSecret?"secret":"open"},"IDoc webhook server listening");}}else {let h=e.port??8808,S=!!e.apiKey||o>0;!e.webhookSecret&&!S&&c.warn({port:h},"IDoc webhook (/idoc) and MCP endpoint have NO authentication. Set --api-key (or tokens in destinations.json) and --webhook-secret (or SAP_WEBHOOK_SECRET) for production."),r.watch(),g=await Pr({transport:s,port:h,apiKey:e.apiKey,destinations:r,serverOptions:f,jwtIssuer:process.env.SAP_JWT_ISSUER,jwtAudience:process.env.SAP_JWT_AUDIENCE,idocStore:d,webhookSecret:e.webhookSecret,webhookMaxBodyBytes:e.webhookMaxBodyBytes}),c.info({transport:s,port:g.port,auth:[...e.apiKey?["api-key"]:[],...o>0?[`tokens(${o})`]:[]].join("+")||"open"},"SAP MCP server listening on HTTP");}for(let h of r.names()){let S=r.get(h);S&&en(r.names().length>1?c.child({destination:h}):c,S,a);}let y=async h=>{c.info({signal:h},"Shutting down...");try{r.close(),u&&await u.closeAll(),g&&await g.close();}catch(S){c.error({err:S},"Shutdown error (continuing)");}process.exit(0);};process.on("SIGTERM",()=>y("SIGTERM")),process.on("SIGINT",()=>y("SIGINT"));}sa().catch(t=>{process.stderr.write(`Fatal error: ${t instanceof Error?t.message:String(t)}
316
+ Documentation, releases, issues: https://github.com/guniweb/guniweb-sap-mcp
317
+ Support: support@guniweb.de \u2014 https://guniweb.de/sap-mcp
318
+ `;async function sa(){let t=process.argv.slice(2);if(t[0]!==void 0&&ln.has(t[0])){let h=await dn(t,{env:process.env,stdout:S=>process.stdout.write(S),stderr:S=>process.stderr.write(S)});process.exit(h);}let e=Vt();e.help&&(process.stdout.write(ra),process.exit(0));let n=[],r=e.destinationsFile?await we.fromFile(e.destinationsFile,{env:process.env,onWarn:h=>n.push(h)}):we.fromEnv(process.env),s=e.transport??"stdio",i=r.defaultDestination(),o=r.tokens().length;if(!i&&(s==="stdio"||o===0))throw new Error(`${r.filePath} definiert ${r.names().length} Destinations (${r.names().join(", ")}), aber keine hei\xDFt "default"${s==="stdio"?' \u2014 im stdio-Betrieb gibt es genau eine Verbindung, also muss eine Destination "default" hei\xDFen oder die Datei genau ein Ziel enthalten.':' und es sind keine Tokens konfiguriert, die eine Anfrage einem Ziel zuordnen k\xF6nnten. Benenne eine Destination "default" oder lege Tokens an.'}`);let a=bt(process.env),c=ps({transport:{target:"pino/file",options:{destination:2}}});a.tlsVerify===false&&c.warn({tlsVerify:false},"SAP_TLS_VERIFY=false: TLS certificate validation disabled. ONLY use for development/test against self-signed certs. Production deployments MUST use SAP_TLS_VERIFY=true (or unset)."),a.clientCert&&c.info({hasCert:true,hasPassphrase:!!a.clientKeyPassphrase},"X.509 client certificate enabled for OData transport"),c.info(i?{baseUrl:i.baseUrl,authType:i.authType}:{destinations:r.names(),tokens:o},"SAP MCP server starting");for(let h of n)c.warn(h);r.source==="file"?(c.info({file:r.filePath,destinations:r.names(),defaultDestination:i?.name??null,tokens:o},"Destinations aus Datei geladen"),r.onChange(h=>{c.info({version:h.version,destinations:h.names,tokens:h.tokenCount},"Destinations neu geladen");}),r.onError(h=>{c.warn({errors:h},"Destinations-Datei ung\xFCltig \u2014 die letzte g\xFCltige Konfiguration bleibt in Kraft");})):c.info({destinations:r.names()},'Destination "default" aus SAP_*-Umgebung (keine destinations.json konfiguriert)'),e.allowWriteExplicit?c.warn("Schreibwerkzeuge sind aktiv (--allow-write): sap_create, sap_update, sap_delete, sap_function und sap_idoc_send k\xF6nnen Daten im SAP-System ver\xE4ndern."):c.info("Nur-Lese-Betrieb (Standard seit 1.0). Schreibwerkzeuge sind nicht registriert; mit --allow-write bzw. SAP_MCP_ALLOW_WRITE=true aktivieren.");for(let h of r.names())Zt(r.names().length>1?c.child({destination:h}):c,r.get(h)?.sapClient);e.transport&&e.transport!=="stdio"&&(process.env.SAP_JWT_ISSUER?c.info({issuer:process.env.SAP_JWT_ISSUER},"Eingehende Benutzer-Token werden gegen die JWKS des Issuers gepr\xFCft"):c.warn("SAP_JWT_ISSUER ist nicht gesetzt: eingehende Bearer-Token werden nur dekodiert, nicht auf ihre Signatur gepr\xFCft. F\xFCr Principal Propagation an SAP ist das vertretbar (SAP pr\xFCft selbst); eine lokale Entscheidung darf sich auf die Identit\xE4t aber nicht st\xFCtzen.")),e.expose&&c.info({exposePatterns:e.expose},"Entity set filtering active");let l=await Tr(c),u;if(l){let h=Ht(process.env);if(h){h.snc_lib&&!existsSync(h.snc_lib)&&c.warn({path:h.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."),u=new ut(h,c);let S=!!h.snc_qop;c.info({ashost:h.ashost,mshost:h.mshost,snc:S,...S?{qop:h.snc_qop,myname:h.snc_myname}:{}},S?"RFC pool ready (SNC-secured)":"RFC pool ready");}else c.info("node-rfc loaded but no SAP_RFC_* config \u2014 RFC tier requires sap_enable_tools rfc + env vars to activate");}let d=new de,m={exposePatterns:e.expose,idocStore:d,sapClient:i?.sapClient,partnerDefaults:{sndpor:e.sndpor,sndprt:e.sndprt,sndprn:e.sndprn,rcvpor:e.rcvpor,rcvprt:e.rcvprt,rcvprn:e.rcvprn},catalogV2Url:e.catalogV2Url,catalogV4Url:e.catalogV4Url,concurrency:e.concurrency,httpOptions:a,onDiscoveryMatch:h=>{c.info({path:h,attemptedPaths:3},"V2 catalog auto-discovery: matched path");}},p=e.toolTimeoutSeconds??et;p===0?c.warn("Zeitbudget je Tool-Aufruf abgeschaltet (--tool-timeout 0): ein Aufruf kann unbegrenzt laufen und die Client-Verbindung bis zu deren eigenem Timeout blockieren."):c.info({toolTimeoutSeconds:p},"Zeitbudget je Tool-Aufruf aktiv"),e.cacheDir?c.info({cacheDir:e.cacheDir},"Cache-Ablage aktiv (\xFCberlebt Neustarts)"):c.info("Kein Cache-Verzeichnis konfiguriert (--cache-dir / SAP_CACHE_DIR): Katalog und Metadaten werden nach jedem Neustart erneut aus SAP geholt.");let f={...m,toolTimeoutMs:p*1e3,cacheDir:e.cacheDir,readOnly:e.readOnly,tiers:e.tiers,hasIdocConfig:qt(e),hasRfcConfig:_r(process.env),rfcAvailable:l,rfcPool:u,logger:c},g;if(s==="stdio"){if(!i)throw new Error("stdio-Betrieb ohne Standard-Destination.");let{server:h}=De(i,f),S=new StdioServerTransport;if(await h.connect(S),c.info("SAP MCP server connected on stdio transport"),e.webhookPort){e.webhookSecret||c.warn({port:e.webhookPort},"IDoc webhook has NO authentication: anyone reaching this port can inject IDocs. Set --webhook-secret (or SAP_WEBHOOK_SECRET) for production.");let v=await hn(e.webhookPort,d,{host:e.webhookHost,secret:e.webhookSecret,maxBodyBytes:e.webhookMaxBodyBytes});c.info({port:v.port,auth:e.webhookSecret?"secret":"open"},"IDoc webhook server listening");}}else {let h=e.port??8808,S=!!e.apiKey||o>0;!e.webhookSecret&&!S&&c.warn({port:h},"IDoc webhook (/idoc) and MCP endpoint have NO authentication. Set --api-key (or tokens in destinations.json) and --webhook-secret (or SAP_WEBHOOK_SECRET) for production."),r.watch(),g=await kr({transport:s,port:h,apiKey:e.apiKey,destinations:r,serverOptions:f,jwtIssuer:process.env.SAP_JWT_ISSUER,jwtAudience:process.env.SAP_JWT_AUDIENCE,idocStore:d,webhookSecret:e.webhookSecret,webhookMaxBodyBytes:e.webhookMaxBodyBytes}),c.info({transport:s,port:g.port,auth:[...e.apiKey?["api-key"]:[],...o>0?[`tokens(${o})`]:[]].join("+")||"open"},"SAP MCP server listening on HTTP");}for(let h of r.names()){let S=r.get(h);S&&en(r.names().length>1?c.child({destination:h}):c,S,a);}let y=async h=>{c.info({signal:h},"Shutting down...");try{r.close(),u&&await u.closeAll(),g&&await g.close();}catch(S){c.error({err:S},"Shutdown error (continuing)");}process.exit(0);};process.on("SIGTERM",()=>y("SIGTERM")),process.on("SIGINT",()=>y("SIGINT"));}sa().catch(t=>{process.stderr.write(`Fatal error: ${t instanceof Error?t.message:String(t)}
318
319
  `),process.exit(1);});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guniweb-sap-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "GuniWeb SAP S/4HANA & ECC MCP Server -- OData V2/V4, IDoc, and RFC/BAPI for n8n",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -76,11 +76,11 @@
76
76
  },
77
77
  "repository": {
78
78
  "type": "git",
79
- "url": "git+https://github.com/jhaertf/guniweb-sap-mcp.git"
79
+ "url": "git+https://github.com/guniweb/guniweb-sap-mcp.git"
80
80
  },
81
- "homepage": "https://github.com/jhaertf/guniweb-sap-mcp#readme",
81
+ "homepage": "https://github.com/guniweb/guniweb-sap-mcp#readme",
82
82
  "bugs": {
83
- "url": "https://github.com/jhaertf/guniweb-sap-mcp/issues"
83
+ "url": "https://github.com/guniweb/guniweb-sap-mcp/issues"
84
84
  },
85
85
  "publishConfig": {
86
86
  "access": "public"