@decionis/mcp 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -36770,7 +36770,7 @@ var MCP_PROTOCOL_VERSIONS = [
36770
36770
  "2025-03-26",
36771
36771
  "2024-11-05"
36772
36772
  ];
36773
- var MCP_DEFAULT_PROTOCOL_VERSION = "2025-06-18";
36773
+ var MCP_DEFAULT_PROTOCOL_VERSION = "2025-11-25";
36774
36774
  var DEFAULT_PUBLIC_BASE_URL = "https://protocol.decionis.com";
36775
36775
  var MCP_HTTP_PATHS = ["/mcp", "/v1/protocol/mcp"];
36776
36776
 
@@ -36974,7 +36974,7 @@ var McpAuthGuard = class {
36974
36974
  return auth?.kind === "org_api_key";
36975
36975
  }
36976
36976
  static findTargetOrgIdForTool(name, args) {
36977
- if (name === "decionis.evaluate_decision" || name === "evaluate_decision" || name === "decionis.agentic_checkout_authorize" || name === "agentic_checkout_authorize" || name === "decionis.evaluate_webex_agent_intent" || name === "evaluate_webex_agent_intent" || name === "decionis.get_decision_dossier" || name === "get_decision_dossier" || name === "decionis.list_enforcement_rollouts" || name === "list_enforcement_rollouts" || name === "decionis.upsert_enforcement_rollout" || name === "upsert_enforcement_rollout" || name === "decionis.resolve_vertical_pack" || name === "resolve_vertical_pack" || name === "decionis.run_shadow_decisioning" || name === "run_shadow_decisioning") {
36977
+ if (name === "decionis.evaluate_decision" || name === "evaluate_decision" || name === "decionis.agentic_checkout_authorize" || name === "agentic_checkout_authorize" || name === "decionis.evaluate_webex_agent_intent" || name === "evaluate_webex_agent_intent" || name === "decionis.get_decision_dossier" || name === "get_decision_dossier" || name === "decionis.get_decision_chain" || name === "get_decision_chain" || name === "decionis.list_enforcement_rollouts" || name === "list_enforcement_rollouts" || name === "decionis.upsert_enforcement_rollout" || name === "upsert_enforcement_rollout" || name === "decionis.resolve_vertical_pack" || name === "resolve_vertical_pack" || name === "decionis.run_shadow_decisioning" || name === "run_shadow_decisioning") {
36978
36978
  return asString(args.org_id);
36979
36979
  }
36980
36980
  return null;
@@ -36983,7 +36983,7 @@ var McpAuthGuard = class {
36983
36983
  if (name === "decionis.evaluate_decision" || name === "evaluate_decision" || name === "decionis.agentic_checkout_authorize" || name === "agentic_checkout_authorize" || name === "decionis.evaluate_webex_agent_intent" || name === "evaluate_webex_agent_intent" || name === "decionis.run_shadow_decisioning" || name === "run_shadow_decisioning") {
36984
36984
  return ["decision:run", "decision:*", "org:*"];
36985
36985
  }
36986
- if (name === "decionis.get_decision_dossier" || name === "get_decision_dossier" || name === "decionis.list_enforcement_rollouts" || name === "list_enforcement_rollouts" || name === "decionis.resolve_vertical_pack" || name === "resolve_vertical_pack") {
36986
+ if (name === "decionis.get_decision_dossier" || name === "get_decision_dossier" || name === "decionis.get_decision_chain" || name === "get_decision_chain" || name === "decionis.list_enforcement_rollouts" || name === "list_enforcement_rollouts" || name === "decionis.resolve_vertical_pack" || name === "resolve_vertical_pack") {
36987
36987
  return ["decision:read", "org:read", "decision:*", "org:*"];
36988
36988
  }
36989
36989
  if (name === "decionis.upsert_enforcement_rollout" || name === "upsert_enforcement_rollout") {
@@ -37080,6 +37080,539 @@ function authorizeResourceRead(auth, uri) {
37080
37080
  // ../protocol/src/mcp/McpRoutes.ts
37081
37081
  import { randomUUID as randomUUID2 } from "crypto";
37082
37082
 
37083
+ // ../../packages/shared/src/Connectors/ConnectorRegistry.ts
37084
+ var SITE = "https://decionis.com";
37085
+ var connectors = [
37086
+ // ── Commerce ──────────────────────────────────────────────────────────
37087
+ {
37088
+ // Live Shopify App Store listing (sweep 2026-08-10, also pinned by
37089
+ // apps/web ShopifyAppStore.listingUrl + the distributionSameAs sweep).
37090
+ // Copy scoped to the published listing description.
37091
+ slug: "shopify",
37092
+ name: "Decionis for Shopify",
37093
+ vendor: "Shopify",
37094
+ category: "commerce",
37095
+ status: "production",
37096
+ summary: "Deterministic policy enforcement on refunds, discounts, oversells, and risky orders at Shopify checkout \u2014 every governed action becomes a signed Decision Dossier with a public verification link.",
37097
+ capabilities: [
37098
+ "margin-floor-enforcement",
37099
+ "checkout-policy-enforcement",
37100
+ "signed-decision-dossier"
37101
+ ],
37102
+ decisionsSupported: ["refund", "discount", "oversell", "risky-order"],
37103
+ installation: [
37104
+ {
37105
+ kind: "marketplace",
37106
+ url: "https://apps.shopify.com/decionis-checkout-gate",
37107
+ label: "Shopify App Store"
37108
+ }
37109
+ ],
37110
+ // Shopify predates the marketplace-target program (it is the original
37111
+ // commerce wedge with its own app workspace), so it deliberately has no
37112
+ // `marketplaceTarget` join — /commerce is its owning page.
37113
+ documentation: `${SITE}/commerce`,
37114
+ distribution: { download: false, guidedInstall: true, remoteInstall: false },
37115
+ activation: { method: "marketplace_install", requiresWorkspace: true },
37116
+ presenceSupport: "planned",
37117
+ marketplaceListingId: "shopify-app",
37118
+ providerId: "SHOPIFY"
37119
+ },
37120
+ {
37121
+ // Published in the WordPress.org plugin directory (slug `decionis`,
37122
+ // 1.0.0) — sweep 2026-08-10 via the distributionSameAs verification.
37123
+ // Copy reuses the shared readiness provenance: verified end to end on a
37124
+ // staging store before the listing went live; shadow first,
37125
+ // enforcement opt-in.
37126
+ slug: "woo",
37127
+ name: "Decionis for WooCommerce",
37128
+ vendor: "WooCommerce",
37129
+ category: "commerce",
37130
+ status: "production",
37131
+ version: "1.0.0",
37132
+ summary: "Margin floors enforced at WooCommerce checkout \u2014 Block and classic, using core COGS, with a signed Decision Dossier per evaluation. New stores start in shadow mode; enforcement is opt-in.",
37133
+ capabilities: [
37134
+ "margin-floor-enforcement",
37135
+ "checkout-policy-enforcement",
37136
+ "shadow-mode",
37137
+ "signed-decision-dossier"
37138
+ ],
37139
+ decisionsSupported: ["checkout-order"],
37140
+ installation: [
37141
+ {
37142
+ kind: "marketplace",
37143
+ url: "https://wordpress.org/plugins/decionis/",
37144
+ label: "WordPress.org Plugin Directory"
37145
+ }
37146
+ ],
37147
+ documentation: `${SITE}/marketplace/woo`,
37148
+ distribution: { download: false, guidedInstall: true, remoteInstall: false },
37149
+ activation: { method: "api_key", requiresWorkspace: true },
37150
+ presenceSupport: "planned",
37151
+ marketplaceTarget: "woo",
37152
+ marketplaceListingId: "woo-plugin",
37153
+ providerId: "WOO"
37154
+ },
37155
+ {
37156
+ // eBay approved the app for production and the connector is deployed and
37157
+ // marketplace-account-deletion compliant; the first read-only Fulfillment
37158
+ // probe passed against a live seller account (docs/marketplace/ebay/
37159
+ // evidence/latest.json). No public eBay listing directory exists, so the
37160
+ // channel is the guided OAuth connect — and enforcement stays honest:
37161
+ // shadow dossiers today, write-scope approval pending.
37162
+ slug: "ebay",
37163
+ name: "Decionis for eBay",
37164
+ vendor: "eBay",
37165
+ category: "commerce",
37166
+ status: "installable",
37167
+ summary: "Margin and promo-hygiene guardrails for eBay Sell-API listings, orders, and promotions \u2014 eBay OAuth connect, production-approved, shadow-mode dossiers with enforcement pending write-scope approval.",
37168
+ capabilities: ["margin-guardrails", "promo-hygiene", "shadow-mode", "signed-decision-dossier"],
37169
+ decisionsSupported: ["listing-change", "order-fulfillment", "promotion"],
37170
+ installation: [
37171
+ {
37172
+ kind: "guided",
37173
+ url: `${SITE}/marketplace/ebay`,
37174
+ label: "eBay OAuth connect (guided)"
37175
+ }
37176
+ ],
37177
+ documentation: `${SITE}/marketplace/ebay`,
37178
+ verification: {
37179
+ validatedBy: "eBay production approval + Decionis live verification",
37180
+ evidence: "eBay approved the app for production; read-only Fulfillment probe verified against a live seller account (docs/marketplace/ebay/evidence/latest.json, deployment 2026-07-14).",
37181
+ lastValidated: "2026-07-14"
37182
+ },
37183
+ distribution: { download: false, guidedInstall: true, remoteInstall: false },
37184
+ activation: { method: "oauth", requiresWorkspace: true },
37185
+ presenceSupport: "planned",
37186
+ marketplaceTarget: "ebay",
37187
+ providerId: "EBAY"
37188
+ },
37189
+ {
37190
+ // Passed ALL Adobe Commerce Marketplace technical review phases (SID
37191
+ // qv4l7d2ak6): automated EQP 2026-07-08, full technical-review
37192
+ // confirmation received 2026-08-10 (tested by Adobe on Magento 2.4.9
37193
+ // EE + CE, PHP 8.5.5). Marketplace release awaits the remaining
37194
+ // marketing review, and the gate has not been validated against a live
37195
+ // merchant store — so no channels yet; the customer-deployable kit
37196
+ // ships next (docs/claims DECIONIS-CLM-022).
37197
+ slug: "adobe-commerce",
37198
+ name: "Decionis Connector for Adobe Commerce",
37199
+ vendor: "Adobe",
37200
+ category: "commerce",
37201
+ status: "validated",
37202
+ version: "1.0.0",
37203
+ summary: "Synchronous checkout-webhook enforcement for Adobe Commerce / Magento \u2014 halts a margin-breaching order (op:exception) or lets it through, failing open on any engine error, with promo-hygiene checks and federated signed Decision Dossiers.",
37204
+ capabilities: [
37205
+ "margin-floor-enforcement",
37206
+ "checkout-policy-enforcement",
37207
+ "promo-hygiene",
37208
+ "fail-open-checkout-gate",
37209
+ "signed-decision-dossier"
37210
+ ],
37211
+ decisionsSupported: ["checkout-order", "pricing-change", "promotion"],
37212
+ installation: [
37213
+ {
37214
+ // The kit page hosts the reviewed composer module + checksum,
37215
+ // manifest, and SBOM — shipped in the same change that makes the URL
37216
+ // resolve (rule 1.1 by construction).
37217
+ kind: "hosted_download",
37218
+ url: `${SITE}/marketplace/adobe-commerce/connector-kit`,
37219
+ label: "Connector kit (guided deployment)"
37220
+ }
37221
+ ],
37222
+ package: {
37223
+ artifact: "/marketplace/adobe-commerce/connector-kit/decionis-module-checkout-guardrail-1.0.0.zip",
37224
+ sha256Path: "/marketplace/adobe-commerce/connector-kit/decionis-module-checkout-guardrail-1.0.0.sha256.txt",
37225
+ manifestPath: "/marketplace/adobe-commerce/connector-kit/manifest.json",
37226
+ sbomPath: "/marketplace/adobe-commerce/connector-kit/SBOM.json",
37227
+ format: "magento2-module"
37228
+ },
37229
+ documentation: `${SITE}/marketplace/adobe-commerce`,
37230
+ verification: {
37231
+ validatedBy: "Adobe Commerce Marketplace technical review (EQP)",
37232
+ evidence: "Passed all technical review phases (SID qv4l7d2ak6): automated EQP pass 2026-07-08, full technical-review confirmation 2026-08-10, tested by Adobe on Magento 2.4.9 Enterprise + Community Edition (PHP 8.5.5). Not yet validated against a live merchant store.",
37233
+ lastValidated: "2026-08-10"
37234
+ },
37235
+ distribution: { download: true, guidedInstall: false, remoteInstall: false },
37236
+ activation: { method: "enrollment_token", requiresWorkspace: true },
37237
+ presenceSupport: "planned",
37238
+ marketplaceTarget: "adobe-commerce",
37239
+ providerId: "ADOBE_COMMERCE"
37240
+ },
37241
+ {
37242
+ // SP-API connector built and unit-tested; shadow-mode first while live
37243
+ // seller validation completes (homepage Beta tile is the owning copy).
37244
+ slug: "amazon",
37245
+ name: "Decionis for Amazon",
37246
+ vendor: "Amazon",
37247
+ category: "commerce",
37248
+ status: "validated",
37249
+ summary: "Margin and pricing guardrails for Amazon SP-API listings and orders \u2014 Login with Amazon connect, shadow-mode first while live seller validation completes.",
37250
+ capabilities: ["margin-guardrails", "shadow-mode", "signed-decision-dossier"],
37251
+ decisionsSupported: ["listing-change", "order"],
37252
+ installation: [],
37253
+ documentation: `${SITE}/marketplace/amazon`,
37254
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
37255
+ activation: { method: "oauth", requiresWorkspace: true },
37256
+ presenceSupport: "planned",
37257
+ marketplaceTarget: "amazon",
37258
+ providerId: "AMAZON"
37259
+ },
37260
+ {
37261
+ // Post-purchase fulfillment circuit breaker — OAuth + refresh, signed
37262
+ // Open API, webhook intake/verification, margin gate, Ship-Package
37263
+ // release built and unit-tested; TikTok App Store review not submitted
37264
+ // (shared readiness provenance).
37265
+ slug: "tiktok",
37266
+ name: "Decionis for TikTok Shop",
37267
+ vendor: "TikTok Shop",
37268
+ category: "commerce",
37269
+ status: "validated",
37270
+ summary: "Post-purchase fulfillment circuit breaker for TikTok Shop \u2014 orders clear a margin gate before Ship-Package release, with signed Decision Dossiers per governed order.",
37271
+ capabilities: ["fulfillment-circuit-breaker", "margin-guardrails", "signed-decision-dossier"],
37272
+ decisionsSupported: ["order-fulfillment"],
37273
+ installation: [],
37274
+ documentation: `${SITE}/marketplace/tiktok`,
37275
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
37276
+ activation: { method: "oauth", requiresWorkspace: true },
37277
+ presenceSupport: "planned",
37278
+ marketplaceTarget: "tiktok",
37279
+ providerId: "TIKTOK_SHOP"
37280
+ },
37281
+ {
37282
+ // Solution Provider OAuth, PO-created webhook intake with HMAC
37283
+ // verification, margin gate, Acknowledge-Order release/hold — built and
37284
+ // unit-tested; Solution Provider approval and live seller verification
37285
+ // remain (shared readiness provenance).
37286
+ slug: "walmart",
37287
+ name: "Decionis for Walmart Marketplace",
37288
+ vendor: "Walmart",
37289
+ category: "commerce",
37290
+ status: "validated",
37291
+ summary: "Post-purchase fulfillment circuit breaker for Walmart Marketplace \u2014 purchase orders clear a margin gate (COGS + referral fee + shipping) before Acknowledge-Order release.",
37292
+ capabilities: ["fulfillment-circuit-breaker", "margin-guardrails", "signed-decision-dossier"],
37293
+ decisionsSupported: ["order-acknowledgement"],
37294
+ installation: [],
37295
+ documentation: `${SITE}/marketplace/walmart`,
37296
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
37297
+ activation: { method: "oauth", requiresWorkspace: true },
37298
+ presenceSupport: "planned",
37299
+ marketplaceTarget: "walmart",
37300
+ providerId: "WALMART_MARKETPLACE"
37301
+ },
37302
+ {
37303
+ // Vendor Center GOP integration — centralized Mastershop OAuth, durable
37304
+ // order polling, multi-market margin/COD/SLA evaluation, guarded
37305
+ // fulfillment; live Vendor Center verification still required before
37306
+ // enforcement (shared readiness provenance).
37307
+ slug: "jumia",
37308
+ name: "Decionis for Jumia",
37309
+ vendor: "Jumia",
37310
+ category: "commerce",
37311
+ status: "validated",
37312
+ summary: "Guarded fulfillment for Jumia Vendor Center \u2014 multi-market margin, COD, and SLA evaluation before order release, with signed Decision Dossiers per governed order.",
37313
+ capabilities: ["fulfillment-circuit-breaker", "margin-guardrails", "signed-decision-dossier"],
37314
+ decisionsSupported: ["order-fulfillment"],
37315
+ installation: [],
37316
+ documentation: `${SITE}/marketplace/jumia`,
37317
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
37318
+ activation: { method: "oauth", requiresWorkspace: true },
37319
+ presenceSupport: "planned",
37320
+ marketplaceTarget: "jumia",
37321
+ providerId: "JUMIA"
37322
+ },
37323
+ {
37324
+ // Headless / Open Checkout gate built and unit-tested; not live-store
37325
+ // validated (rotated off the homepage tiles in favour of WooCommerce).
37326
+ slug: "bigcommerce",
37327
+ name: "Decionis for BigCommerce",
37328
+ vendor: "BigCommerce",
37329
+ category: "commerce",
37330
+ status: "validated",
37331
+ summary: "Checkout margin gate for BigCommerce headless and Open Checkout storefronts \u2014 built and unit-tested, pending validation against a live store.",
37332
+ capabilities: ["margin-floor-enforcement", "checkout-policy-enforcement"],
37333
+ decisionsSupported: ["checkout-order"],
37334
+ installation: [],
37335
+ documentation: `${SITE}/marketplace/bigcommerce`,
37336
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
37337
+ activation: { method: "api_key", requiresWorkspace: true },
37338
+ presenceSupport: "planned",
37339
+ marketplaceTarget: "bigcommerce"
37340
+ },
37341
+ {
37342
+ // Shopware 6 App System handshake, shop-signed webhooks, fail-open
37343
+ // synchronous checkout gate — built and unit-tested, not yet validated
37344
+ // against a live Shopware store (docs/claims DECIONIS-CLM-023).
37345
+ slug: "shopware",
37346
+ name: "Decionis for Shopware",
37347
+ vendor: "Shopware",
37348
+ category: "commerce",
37349
+ status: "validated",
37350
+ summary: "Fail-open synchronous checkout gate for Shopware 6 \u2014 App System registration handshake, shop-signed webhooks, and promo-hygiene checks, pending validation against a live store.",
37351
+ capabilities: [
37352
+ "margin-floor-enforcement",
37353
+ "checkout-policy-enforcement",
37354
+ "promo-hygiene",
37355
+ "fail-open-checkout-gate"
37356
+ ],
37357
+ decisionsSupported: ["checkout-order", "promotion"],
37358
+ installation: [],
37359
+ documentation: `${SITE}/marketplace/shopware`,
37360
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
37361
+ activation: { method: "api_key", requiresWorkspace: true },
37362
+ presenceSupport: "planned",
37363
+ marketplaceTarget: "shopware"
37364
+ },
37365
+ // ── Enterprise ────────────────────────────────────────────────────────
37366
+ {
37367
+ // SAP PartnerEdge Solution Review approved 2026-06-22 (solution id
37368
+ // 2004368, docs/marketplace/sap/solution-hub-listing.md). The PartnerEdge
37369
+ // solution URL is partner-gated (anonymous fetch 404s — sweep
37370
+ // 2026-08-10), so it is deliberately NOT an install channel here; the
37371
+ // customer-deployable BTP deployment kit ships next. Dual-gate wording
37372
+ // from the owning listing copy.
37373
+ slug: "sap",
37374
+ name: "Decionis Governed Execution Gate for SAP",
37375
+ vendor: "SAP",
37376
+ category: "enterprise",
37377
+ status: "validated",
37378
+ version: "0.1.0",
37379
+ summary: "Dual-gate governed execution for SAP S/4HANA, BTP, and CAP-side workflows \u2014 vendor payment release, procurement approval, and finance controls checked before they execute, with a signed Decision Dossier written back to the governed document.",
37380
+ capabilities: [
37381
+ "dual-gate-execution",
37382
+ "segregation-of-duties",
37383
+ "three-way-match",
37384
+ "s4hana-write-back",
37385
+ "signed-decision-dossier"
37386
+ ],
37387
+ decisionsSupported: [
37388
+ "vendor-payment-release",
37389
+ "procurement-approval",
37390
+ "finance-control-change"
37391
+ ],
37392
+ installation: [
37393
+ {
37394
+ // Customer-deployed BTP kit (curated CAP source, mbt build →
37395
+ // cf deploy) — shipped in the same change that makes the URL resolve
37396
+ // (rule 1.1 by construction).
37397
+ kind: "hosted_download",
37398
+ url: `${SITE}/marketplace/sap/deployment-kit`,
37399
+ label: "SAP BTP deployment kit (customer-deployed)"
37400
+ }
37401
+ ],
37402
+ package: {
37403
+ artifact: "/marketplace/sap/deployment-kit/decionis-sap-deployment-kit-0.1.0.zip",
37404
+ sha256Path: "/marketplace/sap/deployment-kit/decionis-sap-deployment-kit-0.1.0.sha256.txt",
37405
+ manifestPath: "/marketplace/sap/deployment-kit/manifest.json",
37406
+ sbomPath: "/marketplace/sap/deployment-kit/SBOM.json",
37407
+ format: "sap-mta-deployment-kit"
37408
+ },
37409
+ documentation: `${SITE}/marketplace/sap`,
37410
+ verification: {
37411
+ validatedBy: "SAP PartnerEdge Solution Review",
37412
+ evidence: "Solution Review completed and approved 2026-06-22 (SAP PartnerEdge solution id 2004368); SAP Store content listing is the next, contract-gated step.",
37413
+ lastValidated: "2026-06-22"
37414
+ },
37415
+ distribution: { download: true, guidedInstall: false, remoteInstall: false },
37416
+ activation: { method: "enrollment_token", requiresWorkspace: true },
37417
+ presenceSupport: "planned",
37418
+ providerId: "SAP"
37419
+ },
37420
+ {
37421
+ // Scoped app x_dec_decionis (tables, script includes, Flow Designer
37422
+ // verify-intent action, shadow observers, workspace dossier panel,
37423
+ // scoped REST API). Self-attested platform evidence — tenant mapping,
37424
+ // ACLs, live policy-check execution, workflow trigger routing — is
37425
+ // recorded in the packaged evidence exports (docs/marketplace/servicenow
37426
+ // submission checklist, evidence refreshed as of the 2026-08-10 sweep).
37427
+ // Store publication deliberately deferred. The customer-deploy kit
37428
+ // CONTENT exists (full-app update-set XML + coverage manifest + INSTALL
37429
+ // under docs/marketplace/servicenow/connector-kit), but the public
37430
+ // download channel + `installable` flip are gated on a real
37431
+ // dev-instance import validation — a user-owned step; never claim the
37432
+ // install before it passes (rule 1.1 on the install claim itself).
37433
+ slug: "servicenow",
37434
+ name: "Decionis Connector for ServiceNow",
37435
+ vendor: "ServiceNow",
37436
+ category: "enterprise",
37437
+ status: "validated",
37438
+ summary: "Scoped ServiceNow app (x_dec_decionis) that wires Decision Dossiers and Decision Drift evidence into incident, change, and risk-case workflows \u2014 a Flow Designer verify-intent action with shadow-mode observers.",
37439
+ capabilities: [
37440
+ "flow-designer-verify-intent",
37441
+ "shadow-mode",
37442
+ "decision-drift-evidence",
37443
+ "signed-decision-dossier"
37444
+ ],
37445
+ decisionsSupported: ["itsm-change", "incident-action", "risk-case-action"],
37446
+ installation: [],
37447
+ documentation: `${SITE}/marketplace/servicenow`,
37448
+ verification: {
37449
+ validatedBy: "Decionis platform evidence (self-attested)",
37450
+ evidence: "Live vendor-instance evidence exports: tenant mapping, ACLs and ACL-role links, live policy-check execution against the authenticated Decionis API, and workflow trigger routing (docs/marketplace/servicenow, evidence current as of 2026-08-10).",
37451
+ lastValidated: "2026-08-10"
37452
+ },
37453
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
37454
+ activation: { method: "enrollment_token", requiresWorkspace: true },
37455
+ presenceSupport: "planned",
37456
+ marketplaceTarget: "servicenow",
37457
+ providerId: "SERVICENOW"
37458
+ },
37459
+ {
37460
+ // Customer-deployed starter package (Apex Action Gate, LWC, platform
37461
+ // event, permission set) served as committed zips with checksums;
37462
+ // AppExchange Security Review in progress, so no marketplace channel is
37463
+ // claimed (homepage Beta tile is the owning copy).
37464
+ slug: "salesforce",
37465
+ name: "Decionis Action Gate for Salesforce",
37466
+ vendor: "Salesforce",
37467
+ category: "enterprise",
37468
+ status: "installable",
37469
+ summary: "Pre-execution Action Gate for Salesforce \u2014 Flow, Apex, and Agentforce actions checked before they run (allow / hold / escalate) with signed Decision Dossiers on the record. Customer-deployed starter package, shadow-first rollout.",
37470
+ capabilities: [
37471
+ "pre-execution-action-gate",
37472
+ "shadow-mode",
37473
+ "agentforce-governance",
37474
+ "signed-decision-dossier"
37475
+ ],
37476
+ decisionsSupported: ["flow-action", "apex-action", "agentforce-action"],
37477
+ installation: [
37478
+ {
37479
+ kind: "hosted_download",
37480
+ url: `${SITE}/marketplace/salesforce/starter-package`,
37481
+ label: "Salesforce starter package (customer-deployed)"
37482
+ }
37483
+ ],
37484
+ package: {
37485
+ artifact: "/marketplace/salesforce/starter-package/decionis-salesforce-starter-package.zip",
37486
+ sha256Path: "/marketplace/salesforce/starter-package/decionis-salesforce-starter-package.sha256.txt",
37487
+ format: "salesforce-metadata-source"
37488
+ },
37489
+ documentation: `${SITE}/marketplace/salesforce`,
37490
+ distribution: { download: true, guidedInstall: false, remoteInstall: false },
37491
+ activation: { method: "api_key", requiresWorkspace: true },
37492
+ presenceSupport: "planned",
37493
+ marketplaceTarget: "salesforce",
37494
+ marketplaceListingId: "salesforce-app",
37495
+ providerId: "SALESFORCE"
37496
+ },
37497
+ // ── Automation ────────────────────────────────────────────────────────
37498
+ {
37499
+ // Live Zapier app-directory listing (sweep 2026-08-10: page resolves and
37500
+ // shows the Decionis app with the Create Decision Dossier action).
37501
+ slug: "zapier",
37502
+ name: "Decionis for Zapier",
37503
+ vendor: "Zapier",
37504
+ category: "automation",
37505
+ status: "production",
37506
+ summary: "Create a governed decision and signed Decision Dossier inside any Zap \u2014 OAuth connection, Shadow Mode by default, and idempotent replay for temporary service failures.",
37507
+ capabilities: ["automation-step-governance", "shadow-mode", "signed-decision-dossier"],
37508
+ decisionsSupported: ["automation-step"],
37509
+ installation: [
37510
+ {
37511
+ kind: "marketplace",
37512
+ url: "https://zapier.com/apps/decionis/integrations",
37513
+ label: "Zapier app directory"
37514
+ }
37515
+ ],
37516
+ documentation: `${SITE}/docs/zapier`,
37517
+ distribution: { download: false, guidedInstall: true, remoteInstall: false },
37518
+ activation: { method: "oauth", requiresWorkspace: true },
37519
+ presenceSupport: "planned",
37520
+ marketplaceListingId: "zapier"
37521
+ },
37522
+ {
37523
+ // The native Make app is built with its OAuth on-ramp live, but the Make
37524
+ // listing is private in review — so the installable path today is the
37525
+ // documented HTTP recipe (AutomationConnectors owning copy, status
37526
+ // `preview`).
37527
+ slug: "make",
37528
+ name: "Decionis for Make",
37529
+ vendor: "Make",
37530
+ category: "automation",
37531
+ status: "installable",
37532
+ summary: "Evaluate an Action before the protected module runs \u2014 the native Decionis app is in Make listing review, and the documented HTTP recipe calls the same gate and returns the same signed Decision Dossier today.",
37533
+ capabilities: ["automation-step-governance", "signed-decision-dossier"],
37534
+ decisionsSupported: ["automation-step"],
37535
+ installation: [
37536
+ {
37537
+ kind: "docs",
37538
+ url: `${SITE}/docs/automation-connectors#make`,
37539
+ label: "HTTP recipe (native app listing in review)"
37540
+ }
37541
+ ],
37542
+ documentation: `${SITE}/docs/automation-connectors#make`,
37543
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
37544
+ activation: { method: "oauth", requiresWorkspace: true },
37545
+ presenceSupport: "planned"
37546
+ },
37547
+ {
37548
+ // Community node published on npm (registry sweep 2026-08-10:
37549
+ // n8n-nodes-decionis@0.1.0).
37550
+ slug: "n8n",
37551
+ name: "Decionis for n8n",
37552
+ vendor: "n8n",
37553
+ category: "automation",
37554
+ status: "production",
37555
+ version: "0.1.0",
37556
+ summary: "Every automation step asks permission before it executes \u2014 install the n8n community node from npm, or start from the HTTP Request recipe and four importable example workflows.",
37557
+ capabilities: ["automation-step-governance", "signed-decision-dossier"],
37558
+ decisionsSupported: ["automation-step"],
37559
+ installation: [
37560
+ {
37561
+ kind: "npm",
37562
+ url: "https://www.npmjs.com/package/n8n-nodes-decionis",
37563
+ label: "n8n community node on npm"
37564
+ }
37565
+ ],
37566
+ documentation: `${SITE}/docs/automation-connectors#n8n`,
37567
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
37568
+ activation: { method: "api_key", requiresWorkspace: true },
37569
+ presenceSupport: "planned"
37570
+ },
37571
+ // ── Platform ──────────────────────────────────────────────────────────
37572
+ {
37573
+ // The standard webhook path into the protocol — the same Gate 1
37574
+ // admission check, any backend (owning copy: ERP & Webhooks tile +
37575
+ // /docs/webhooks).
37576
+ // `installable`, not `production`: the webhook path is live product
37577
+ // capability, but there is no public marketplace/registry listing to
37578
+ // install it from — you wire it from the documented recipe.
37579
+ slug: "webhooks",
37580
+ name: "ERP & Webhooks",
37581
+ vendor: "Any system of record",
37582
+ category: "platform",
37583
+ status: "installable",
37584
+ summary: "Cross-verify cost basis and inventory against your system of record via standard webhooks \u2014 the same Gate 1 admission check, any backend.",
37585
+ capabilities: ["erp-cross-verification", "webhook-intake"],
37586
+ decisionsSupported: ["cost-basis-verification", "inventory-verification"],
37587
+ installation: [
37588
+ { kind: "docs", url: `${SITE}/docs/webhooks`, label: "Webhook integration guide" }
37589
+ ],
37590
+ documentation: `${SITE}/docs/webhooks`,
37591
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
37592
+ activation: { method: "api_key", requiresWorkspace: true },
37593
+ presenceSupport: "planned",
37594
+ providerId: "WEBHOOK"
37595
+ }
37596
+ ];
37597
+ var ConnectorRegistry = class {
37598
+ static connectors = connectors;
37599
+ static bySlug(slug) {
37600
+ return connectors.find((connector) => connector.slug === slug);
37601
+ }
37602
+ static byCategory(category) {
37603
+ return connectors.filter((connector) => connector.category === category);
37604
+ }
37605
+ static byStatus(status) {
37606
+ return connectors.filter((connector) => connector.status === status);
37607
+ }
37608
+ static slugs() {
37609
+ return connectors.map((connector) => connector.slug);
37610
+ }
37611
+ };
37612
+ var CONNECTOR_REGISTRY = ConnectorRegistry.connectors;
37613
+ var getConnector = ConnectorRegistry.bySlug;
37614
+ var CONNECTOR_SLUGS = ConnectorRegistry.slugs();
37615
+
37083
37616
  // ../protocol/src/mcp/McpResources.ts
37084
37617
  var McpResources = class {
37085
37618
  static buildStaticResources(publicBaseUrl, serviceName, serviceVersion) {
@@ -37119,6 +37652,13 @@ var McpResources = class {
37119
37652
  description: "Available vertical packs and workflow overlays exposed by Decionis Protocol.",
37120
37653
  mimeType: "application/json"
37121
37654
  },
37655
+ {
37656
+ uri: "decionis://connectors/registry",
37657
+ name: "connector_registry",
37658
+ title: "Decionis Connector Registry",
37659
+ description: "Every Decionis connector with availability status, install channels, governed actions, and verification evidence \u2014 the same registry that generates decionis.com/integrations.",
37660
+ mimeType: "application/json"
37661
+ },
37122
37662
  {
37123
37663
  uri: "decionis://protocol/webex-mcp-registration",
37124
37664
  name: "webex_mcp_registration",
@@ -37341,7 +37881,8 @@ var McpResources = class {
37341
37881
  "resources/read -> decionis://protocol/webex-agent-intent-contract",
37342
37882
  "resources/read -> decionis://protocol/webex-reviewer-scenarios",
37343
37883
  "tools/call -> decionis.evaluate_webex_agent_intent",
37344
- "tools/call -> decionis.get_decision_dossier"
37884
+ "tools/call -> decionis.get_decision_dossier",
37885
+ "tools/call -> decionis.get_decision_chain"
37345
37886
  ]
37346
37887
  }
37347
37888
  };
@@ -37367,10 +37908,34 @@ var McpResources = class {
37367
37908
  });
37368
37909
  return { mimeType: "application/json", value: response.body };
37369
37910
  }
37911
+ if (uri === "decionis://connectors/registry") {
37912
+ return {
37913
+ mimeType: "application/json",
37914
+ value: {
37915
+ registry: "decionis-connectors",
37916
+ statuses: {
37917
+ production: "generally available today through a live public channel",
37918
+ installable: "customer-deployable today outside public marketplaces",
37919
+ validated: "built and verified (see each connector's verification); downloadable when a package is listed",
37920
+ planned: "roadmap; never carries install channels"
37921
+ },
37922
+ connectors: CONNECTOR_REGISTRY,
37923
+ human_catalogue: "https://decionis.com/integrations"
37924
+ }
37925
+ };
37926
+ }
37370
37927
  const parsed = new URL(uri);
37371
37928
  if (parsed.protocol !== "decionis:") {
37372
37929
  throw new Error(`Unsupported resource URI '${uri}'`);
37373
37930
  }
37931
+ if (parsed.hostname === "connectors") {
37932
+ const slug = decodeURIComponent(parsed.pathname.replace(/^\//, ""));
37933
+ const connector = getConnector(slug);
37934
+ if (!connector) {
37935
+ throw new Error(`Unknown connector '${slug}'`);
37936
+ }
37937
+ return { mimeType: "application/json", value: connector };
37938
+ }
37374
37939
  if (parsed.hostname === "marketplaces" && parsed.pathname.startsWith("/targets/")) {
37375
37940
  const target = decodeURIComponent(parsed.pathname.replace("/targets/", ""));
37376
37941
  const response = await invokeProtocolRoute(app, {
@@ -37640,7 +38205,7 @@ var McpTools = class {
37640
38205
  return cart && typeof cart.amount === "number" ? cart.amount : void 0;
37641
38206
  }
37642
38207
  static buildTools(app) {
37643
- return [
38208
+ const tools = [
37644
38209
  {
37645
38210
  name: "decionis.evaluate_decision",
37646
38211
  aliases: ["evaluate_decision"],
@@ -37896,6 +38461,37 @@ var McpTools = class {
37896
38461
  return response.body;
37897
38462
  }
37898
38463
  },
38464
+ {
38465
+ name: "decionis.get_decision_chain",
38466
+ aliases: ["get_decision_chain"],
38467
+ title: "Get Decision Chain",
38468
+ description: "Read the workflow lineage for linked Decionis decisions: the chain summary of evaluations, their outcomes, and the dossiers each sealed. The read-only status view for a multi-step workflow an agent is participating in.",
38469
+ inputSchema: {
38470
+ type: "object",
38471
+ required: ["org_id", "chain_id"],
38472
+ additionalProperties: false,
38473
+ properties: {
38474
+ org_id: { type: "string", description: "Org UUID." },
38475
+ chain_id: { type: "string", description: "Decision chain UUID." }
38476
+ }
38477
+ },
38478
+ outputSchema: { type: "object", additionalProperties: true },
38479
+ async handler(args) {
38480
+ const orgId = asString(args.org_id);
38481
+ const chainId = asString(args.chain_id);
38482
+ if (!orgId || !chainId) {
38483
+ throw new Error("org_id and chain_id are required");
38484
+ }
38485
+ const response = await invokeProtocolRoute(app, {
38486
+ method: "GET",
38487
+ url: `/v1/protocol/decision-chains/${encodeURIComponent(chainId)}?org_id=${encodeURIComponent(orgId)}`
38488
+ });
38489
+ if (!response.ok) {
38490
+ throw new Error(`Decision chain lookup failed (${response.statusCode})`);
38491
+ }
38492
+ return response.body;
38493
+ }
38494
+ },
37899
38495
  {
37900
38496
  name: "decionis.list_enforcement_rollouts",
37901
38497
  aliases: ["list_enforcement_rollouts"],
@@ -38114,6 +38710,29 @@ var McpTools = class {
38114
38710
  }
38115
38711
  }
38116
38712
  ];
38713
+ const readOnlyTools = /* @__PURE__ */ new Set([
38714
+ "decionis.get_decision_dossier",
38715
+ "decionis.get_decision_chain",
38716
+ "decionis.list_enforcement_rollouts",
38717
+ "decionis.list_marketplace_targets",
38718
+ "decionis.get_marketplace_target",
38719
+ "decionis.list_marketplace_readiness",
38720
+ "decionis.list_vertical_packs",
38721
+ "decionis.resolve_vertical_pack"
38722
+ ]);
38723
+ const idempotentWriteTools = /* @__PURE__ */ new Set(["decionis.upsert_enforcement_rollout"]);
38724
+ return tools.map((tool) => {
38725
+ const readOnly = readOnlyTools.has(tool.name);
38726
+ return {
38727
+ ...tool,
38728
+ annotations: {
38729
+ readOnlyHint: readOnly,
38730
+ destructiveHint: false,
38731
+ idempotentHint: readOnly || idempotentWriteTools.has(tool.name),
38732
+ openWorldHint: false
38733
+ }
38734
+ };
38735
+ });
38117
38736
  }
38118
38737
  static buildTextToolResult(value, options) {
38119
38738
  const text = toPrettyText(value);
@@ -38167,6 +38786,37 @@ function findTool(tools, name) {
38167
38786
  }
38168
38787
 
38169
38788
  // ../protocol/src/mcp/McpRoutes.ts
38789
+ async function recordTelemetry(app, sink, event) {
38790
+ if (!sink) return;
38791
+ try {
38792
+ await sink.record(event);
38793
+ } catch (err) {
38794
+ app.log.warn({ err, event: event.event }, "MCP telemetry write failed");
38795
+ }
38796
+ }
38797
+ function canonicalAuthorizationOutcome(value) {
38798
+ const record = asRecord(value);
38799
+ const raw = asString(record?.outcome) ?? asString(record?.decision) ?? asString(record?.verdict);
38800
+ if (!raw) return null;
38801
+ switch (raw.trim().toUpperCase()) {
38802
+ case "ALLOW":
38803
+ case "APPROVE":
38804
+ case "AUTO_APPROVE":
38805
+ return "ALLOW";
38806
+ case "BLOCK":
38807
+ case "REJECT":
38808
+ case "AUTO_REJECT":
38809
+ return "BLOCK";
38810
+ case "ESCALATE":
38811
+ return "ESCALATE";
38812
+ case "REVIEW":
38813
+ case "RESTRAIN":
38814
+ case "REQUIRE_REVIEW":
38815
+ return "REVIEW";
38816
+ default:
38817
+ return null;
38818
+ }
38819
+ }
38170
38820
  function buildInitializeResult(serviceName, serviceVersion) {
38171
38821
  return {
38172
38822
  protocolVersion: MCP_DEFAULT_PROTOCOL_VERSION,
@@ -38180,7 +38830,7 @@ function buildInitializeResult(serviceName, serviceVersion) {
38180
38830
  title: "Decionis Protocol MCP",
38181
38831
  version: serviceVersion
38182
38832
  },
38183
- instructions: "Use Decionis Protocol as the deterministic execution-governance MCP server. Encode policy with evaluate_decision, stage enforcement with upsert_enforcement_rollout, and verify evidence through Decision Dossiers and audit-linked resources."
38833
+ instructions: "Use Decionis Protocol to evaluate a proposed action before execution and inspect its signed governance evidence. Evaluation tools record Decionis decisions but never perform the downstream mutation. Use read-only dossier, chain, marketplace, and vertical tools for inspection."
38184
38834
  };
38185
38835
  }
38186
38836
  async function handleJsonRpcRequest(app, requestBody, options) {
@@ -38218,6 +38868,16 @@ async function handleJsonRpcRequest(app, requestBody, options) {
38218
38868
  }
38219
38869
  const requestedVersion = asString(params.protocolVersion);
38220
38870
  const negotiatedVersion = requestedVersion && MCP_PROTOCOL_VERSIONS.includes(requestedVersion) ? requestedVersion : MCP_DEFAULT_PROTOCOL_VERSION;
38871
+ const clientInfo = asRecord(params.clientInfo);
38872
+ await recordTelemetry(app, options.telemetry, {
38873
+ event: "connection_succeeded",
38874
+ connectionId: options.connectionId,
38875
+ clientName: asString(clientInfo?.name),
38876
+ clientVersion: asString(clientInfo?.version),
38877
+ protocolVersion: negotiatedVersion,
38878
+ authMode: options.auth?.kind ?? "none",
38879
+ statusCode: 200
38880
+ });
38221
38881
  return {
38222
38882
  statusCode: 200,
38223
38883
  payload: jsonRpcSuccess(id, {
@@ -38233,6 +38893,12 @@ async function handleJsonRpcRequest(app, requestBody, options) {
38233
38893
  case "ping":
38234
38894
  return { statusCode: 200, payload: jsonRpcSuccess(id, {}) };
38235
38895
  case "tools/list":
38896
+ await recordTelemetry(app, options.telemetry, {
38897
+ event: "tools_listed",
38898
+ connectionId: options.connectionId,
38899
+ authMode: options.auth?.kind ?? "none",
38900
+ statusCode: 200
38901
+ });
38236
38902
  return {
38237
38903
  statusCode: 200,
38238
38904
  payload: jsonRpcSuccess(id, {
@@ -38241,7 +38907,8 @@ async function handleJsonRpcRequest(app, requestBody, options) {
38241
38907
  title: tool.title,
38242
38908
  description: tool.description,
38243
38909
  inputSchema: tool.inputSchema,
38244
- ...tool.outputSchema ? { outputSchema: tool.outputSchema } : {}
38910
+ ...tool.outputSchema ? { outputSchema: tool.outputSchema } : {},
38911
+ annotations: tool.annotations
38245
38912
  }))
38246
38913
  })
38247
38914
  };
@@ -38260,15 +38927,61 @@ async function handleJsonRpcRequest(app, requestBody, options) {
38260
38927
  payload: jsonRpcError(id, -32601, `Unknown tool '${name}'`)
38261
38928
  };
38262
38929
  }
38930
+ const toolStartedAt = process.hrtime.bigint();
38931
+ await recordTelemetry(app, options.telemetry, {
38932
+ event: "tool_called",
38933
+ connectionId: options.connectionId,
38934
+ toolName: tool.name,
38935
+ authMode: options.auth?.kind ?? "none"
38936
+ });
38263
38937
  const args = asRecord(params.arguments) ?? {};
38264
38938
  const authError = authorizeToolInvocation(options.auth, name, args);
38265
38939
  if (authError) {
38940
+ await recordTelemetry(app, options.telemetry, {
38941
+ event: "authorization_outcome",
38942
+ connectionId: options.connectionId,
38943
+ toolName: tool.name,
38944
+ outcome: "DENIED",
38945
+ authMode: options.auth?.kind ?? "none",
38946
+ statusCode: 403,
38947
+ durationMs: durationMs(toolStartedAt)
38948
+ });
38949
+ await recordTelemetry(app, options.telemetry, {
38950
+ event: "tool_failed",
38951
+ connectionId: options.connectionId,
38952
+ toolName: tool.name,
38953
+ outcome: "DENIED",
38954
+ authMode: options.auth?.kind ?? "none",
38955
+ statusCode: 403,
38956
+ durationMs: durationMs(toolStartedAt)
38957
+ });
38266
38958
  return {
38267
38959
  statusCode: 403,
38268
38960
  payload: jsonRpcError(id, authError.code, authError.message)
38269
38961
  };
38270
38962
  }
38271
38963
  const result = await handleToolCall(tool, args);
38964
+ const outcome = canonicalAuthorizationOutcome(result.structuredContent);
38965
+ await recordTelemetry(app, options.telemetry, {
38966
+ event: result.isError ? "tool_failed" : "tool_succeeded",
38967
+ connectionId: options.connectionId,
38968
+ toolName: tool.name,
38969
+ outcome,
38970
+ authMode: options.auth?.kind ?? "none",
38971
+ statusCode: 200,
38972
+ durationMs: durationMs(toolStartedAt)
38973
+ });
38974
+ if (outcome) {
38975
+ await recordTelemetry(app, options.telemetry, {
38976
+ event: "authorization_outcome",
38977
+ connectionId: options.connectionId,
38978
+ toolName: tool.name,
38979
+ outcome,
38980
+ authMode: options.auth?.kind ?? "none",
38981
+ statusCode: 200,
38982
+ durationMs: durationMs(toolStartedAt)
38983
+ });
38984
+ }
38272
38985
  return {
38273
38986
  statusCode: 200,
38274
38987
  payload: jsonRpcSuccess(id, result)
@@ -38284,6 +38997,13 @@ async function handleJsonRpcRequest(app, requestBody, options) {
38284
38997
  statusCode: 200,
38285
38998
  payload: jsonRpcSuccess(id, {
38286
38999
  resourceTemplates: [
39000
+ {
39001
+ uriTemplate: "decionis://connectors/{slug}",
39002
+ name: "connector_detail",
39003
+ title: "Connector Detail",
39004
+ description: "Read one Connector Registry row: status, install channels, governed actions, verification.",
39005
+ mimeType: "application/json"
39006
+ },
38287
39007
  {
38288
39008
  uriTemplate: "decionis://marketplaces/targets/{target}",
38289
39009
  name: "marketplace_target_detail",
@@ -38445,6 +39165,11 @@ async function handleMcpHttpRequest(app, req, reply, options) {
38445
39165
  const auth = await authenticateMcpRequest(req, authLookup);
38446
39166
  const authDurationMs = durationMs(authStartedAt);
38447
39167
  if (!auth && isAuthenticationRequired(authLookup)) {
39168
+ await recordTelemetry(app, options.telemetry ?? null, {
39169
+ event: "authentication_failed",
39170
+ statusCode: 401,
39171
+ durationMs: authDurationMs
39172
+ });
38448
39173
  const maybeId = asRecord(req.body)?.id;
38449
39174
  const id = typeof maybeId === "string" || typeof maybeId === "number" ? maybeId : null;
38450
39175
  reply.header(
@@ -38454,6 +39179,10 @@ async function handleMcpHttpRequest(app, req, reply, options) {
38454
39179
  stages: { auth_ms: authDurationMs }
38455
39180
  })
38456
39181
  );
39182
+ reply.header(
39183
+ "www-authenticate",
39184
+ `Bearer resource_metadata="${options.publicBaseUrl}/.well-known/oauth-protected-resource", error="invalid_token"`
39185
+ );
38457
39186
  return reply.code(401).send(jsonRpcError(id, -32001, "Unauthorized MCP request"));
38458
39187
  }
38459
39188
  if (Array.isArray(req.body)) {
@@ -38468,7 +39197,35 @@ async function handleMcpHttpRequest(app, req, reply, options) {
38468
39197
  }
38469
39198
  if (isLegacyToolCall(req.body)) {
38470
39199
  const rpcStartedAt2 = process.hrtime.bigint();
39200
+ const connectionId2 = asString(req.headers["mcp-session-id"]);
39201
+ await recordTelemetry(app, options.telemetry ?? null, {
39202
+ event: "tool_called",
39203
+ connectionId: connectionId2,
39204
+ toolName: req.body.tool_name,
39205
+ authMode: auth?.kind ?? "none"
39206
+ });
38471
39207
  const payload = await handleLegacyToolCall(app, auth, req.body);
39208
+ const outcome = canonicalAuthorizationOutcome(payload.output);
39209
+ await recordTelemetry(app, options.telemetry ?? null, {
39210
+ event: payload.is_error || payload.error ? "tool_failed" : "tool_succeeded",
39211
+ connectionId: connectionId2,
39212
+ toolName: req.body.tool_name,
39213
+ outcome,
39214
+ authMode: auth?.kind ?? "none",
39215
+ statusCode: 200,
39216
+ durationMs: durationMs(rpcStartedAt2)
39217
+ });
39218
+ if (outcome) {
39219
+ await recordTelemetry(app, options.telemetry ?? null, {
39220
+ event: "authorization_outcome",
39221
+ connectionId: connectionId2,
39222
+ toolName: req.body.tool_name,
39223
+ outcome,
39224
+ authMode: auth?.kind ?? "none",
39225
+ statusCode: 200,
39226
+ durationMs: durationMs(rpcStartedAt2)
39227
+ });
39228
+ }
38472
39229
  reply.header(
38473
39230
  "server-timing",
38474
39231
  buildServerTimingHeader({
@@ -38493,13 +39250,21 @@ async function handleMcpHttpRequest(app, req, reply, options) {
38493
39250
  return reply.code(400).send(jsonRpcError(null, -32600, "JSON-RPC request body required"));
38494
39251
  }
38495
39252
  const rpcStartedAt = process.hrtime.bigint();
39253
+ const requestedMethod = asString(requestBody.method);
39254
+ const incomingSession = asString(req.headers["mcp-session-id"]);
39255
+ const connectionId = requestedMethod === "initialize" ? randomUUID2() : incomingSession;
38496
39256
  const result = await handleJsonRpcRequest(app, requestBody, {
38497
39257
  publicBaseUrl: options.publicBaseUrl,
38498
39258
  serviceName: options.serviceName,
38499
39259
  serviceVersion: options.serviceVersion,
38500
39260
  auth,
38501
- authLookup
39261
+ authLookup,
39262
+ telemetry: options.telemetry ?? null,
39263
+ connectionId
38502
39264
  });
39265
+ if (requestedMethod === "initialize" && result.statusCode === 200) {
39266
+ reply.header("mcp-session-id", connectionId);
39267
+ }
38503
39268
  reply.header(
38504
39269
  "server-timing",
38505
39270
  buildServerTimingHeader({
@@ -38529,8 +39294,21 @@ function registerProtocolMcpRoutes(app, options) {
38529
39294
  endpoint: `${publicBaseUrl}/v1/protocol/mcp`,
38530
39295
  endpoint_aliases: MCP_HTTP_PATHS,
38531
39296
  supported_protocol_versions: [...MCP_PROTOCOL_VERSIONS],
38532
- auth
39297
+ auth,
39298
+ oauth: {
39299
+ protected_resource_metadata: `${publicBaseUrl}/.well-known/oauth-protected-resource`,
39300
+ authorization_server_metadata: "https://api.decionis.com/.well-known/oauth-authorization-server"
39301
+ }
38533
39302
  };
39303
+ const oauthProtectedResource = {
39304
+ resource: `${publicBaseUrl}/mcp`,
39305
+ authorization_servers: ["https://api.decionis.com"],
39306
+ bearer_methods_supported: ["header"],
39307
+ scopes_supported: ["decision:read", "decision:run", "decision:write"],
39308
+ resource_documentation: "https://decionis.com/docs/protocol-mcp"
39309
+ };
39310
+ app.get("/.well-known/oauth-protected-resource", async () => oauthProtectedResource);
39311
+ app.get("/.well-known/oauth-protected-resource/mcp", async () => oauthProtectedResource);
38534
39312
  for (const path2 of MCP_HTTP_PATHS) {
38535
39313
  app.get(path2, async () => metadata);
38536
39314
  app.delete(path2, async (_req, reply) => reply.code(204).send());
@@ -38540,7 +39318,8 @@ function registerProtocolMcpRoutes(app, options) {
38540
39318
  publicBaseUrl,
38541
39319
  serviceName: options.serviceName,
38542
39320
  serviceVersion: options.serviceVersion,
38543
- authLookup
39321
+ authLookup,
39322
+ telemetry: options.telemetry ?? null
38544
39323
  })
38545
39324
  );
38546
39325
  }
@@ -46731,7 +47510,8 @@ function buildDecisionDossierPersistedProofBundle(input) {
46731
47510
  }
46732
47511
 
46733
47512
  // ../protocol/src/support/PanDetection.ts
46734
- var PanDetection = class {
47513
+ var PanDetection = class _PanDetection {
47514
+ static UUID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
46735
47515
  static toDigits(value) {
46736
47516
  if (typeof value !== "string" && typeof value !== "number") return null;
46737
47517
  const digits = String(value).replace(/\D/g, "");
@@ -46753,12 +47533,32 @@ var PanDetection = class {
46753
47533
  }
46754
47534
  return sum % 10 === 0;
46755
47535
  }
47536
+ /**
47537
+ * Candidate card numbers inside a value: runs of digits separated only by the
47538
+ * characters a card number is actually written with (spaces and dashes).
47539
+ *
47540
+ * Splitting on everything else is the whole point. Stripping every non-digit
47541
+ * from the value instead — which this used to do — welds unrelated digits
47542
+ * together across letters and punctuation, and a UUID is the case that bites:
47543
+ * discard its letters and roughly 4% of them are a 13-to-19-digit string that
47544
+ * satisfies Luhn. Since we put UUIDs into decision context ourselves (the
47545
+ * duplicate-payment fact carries the prior dossier id), that turned a
47546
+ * legitimate duplicate-payment decision into "raw PAN-like value detected"
47547
+ * about one time in twenty-five.
47548
+ */
47549
+ static panCandidates(value) {
47550
+ if (typeof value === "number") return [String(value).replace(/[^\d]/g, "")];
47551
+ if (typeof value !== "string") return [];
47552
+ if (_PanDetection.UUID_SHAPE.test(value.trim())) return [];
47553
+ const runs = value.match(/(?<![0-9A-Za-z])\d[\d \-]*\d(?![0-9A-Za-z])/g) ?? [];
47554
+ return runs.map((run) => run.replace(/[^\d]/g, "")).filter((digits) => digits.length > 0);
47555
+ }
46756
47556
  static looksLikeRawPan(value) {
46757
- const digits = toDigits(value);
46758
- if (!digits) return false;
46759
- if (digits.length < 13 || digits.length > 19) return false;
46760
- if (/^(\d)\1+$/.test(digits)) return false;
46761
- return isLuhnValid(digits);
47557
+ return _PanDetection.panCandidates(value).some((digits) => {
47558
+ if (digits.length < 13 || digits.length > 19) return false;
47559
+ if (/^(\d)\1+$/.test(digits)) return false;
47560
+ return isLuhnValid(digits);
47561
+ });
46762
47562
  }
46763
47563
  static findRawPanPath(value, path2, visited = /* @__PURE__ */ new Set()) {
46764
47564
  if (looksLikeRawPan(value)) return path2;
@@ -46779,9 +47579,6 @@ var PanDetection = class {
46779
47579
  return null;
46780
47580
  }
46781
47581
  };
46782
- function toDigits(value) {
46783
- return PanDetection.toDigits(value);
46784
- }
46785
47582
  function isLuhnValid(digits) {
46786
47583
  return PanDetection.isLuhnValid(digits);
46787
47584
  }
@@ -52057,10 +52854,15 @@ var readiness = {
52057
52854
  "adobe-commerce": {
52058
52855
  target: "adobe-commerce",
52059
52856
  protocol_capture: "CAPTURED",
52857
+ // adapter_state stays BUILDING deliberately: Adobe's Marketplace
52858
+ // technical review passed in full (all phases, SID qv4l7d2ak6 — automated
52859
+ // EQP 2026-07-08, final confirmation 2026-08-10, tested on Magento 2.4.9
52860
+ // EE + CE / PHP 8.5.5), but the gate has not yet run against a live
52861
+ // merchant store, and that is the bar LIVE means here.
52060
52862
  adapter_state: "BUILDING",
52061
- docs_state: "PLANNED",
52863
+ docs_state: "BUILDING",
52062
52864
  launch_state: "PLANNED",
52063
- next_milestone: "Shadow\u2192enforce control shipped; onboard a live Adobe Commerce store in shadow, verify the checkout gate, then promote it to enforcement"
52865
+ next_milestone: "All Adobe Marketplace technical review phases passed (SID qv4l7d2ak6); ship the customer-deployable connector kit, onboard a live Adobe Commerce store in shadow, verify the checkout gate, then promote it to enforcement"
52064
52866
  },
52065
52867
  shopware: {
52066
52868
  target: "shopware",
@@ -52081,10 +52883,16 @@ var readiness = {
52081
52883
  woo: {
52082
52884
  target: "woo",
52083
52885
  protocol_capture: "CAPTURED",
52084
- adapter_state: "BUILDING",
52085
- docs_state: "PLANNED",
52086
- launch_state: "PLANNED",
52087
- next_milestone: "Verify native authorization plus Block and classic checkout parity against a live WooCommerce store before enabling enforcement"
52886
+ // Truth-up 2026-08-10: the plugin is published in the WordPress.org
52887
+ // directory (slug `decionis`, 1.0.0) and Block + classic checkout,
52888
+ // core COGS, HPOS order CRUD, and the signed dossier were verified end
52889
+ // to end on a staging store — including an admin-enabled ENFORCEMENT
52890
+ // block — before the listing went live (shared readiness provenance).
52891
+ // The previous PLANNED states predated the public listing.
52892
+ adapter_state: "LIVE",
52893
+ docs_state: "LIVE",
52894
+ launch_state: "BUILDING",
52895
+ next_milestone: "WordPress.org listing is live and self-serve; grow installs and collect the first live-store enforcement evidence for the listing's proof section"
52088
52896
  },
52089
52897
  jumia: {
52090
52898
  target: "jumia",
@@ -52786,7 +53594,8 @@ function buildProtocolApp(options) {
52786
53594
  registerProtocolMcpRoutes(app, {
52787
53595
  serviceName: PROTOCOL_SERVICE,
52788
53596
  serviceVersion: PROTOCOL_VERSION,
52789
- authLookup: options?.mcpAuthLookup ?? null
53597
+ authLookup: options?.mcpAuthLookup ?? null,
53598
+ telemetry: options?.mcpTelemetry ?? null
52790
53599
  });
52791
53600
  const executionAuthority = createExecutionGatingService(options?.executionGating);
52792
53601
  registerExecutionGatingRoutes(app, {