@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.
@@ -549,7 +549,7 @@ var MCP_PROTOCOL_VERSIONS = [
549
549
  "2025-03-26",
550
550
  "2024-11-05"
551
551
  ];
552
- var MCP_DEFAULT_PROTOCOL_VERSION = "2025-06-18";
552
+ var MCP_DEFAULT_PROTOCOL_VERSION = "2025-11-25";
553
553
  var DEFAULT_PUBLIC_BASE_URL = "https://protocol.decionis.com";
554
554
  var MCP_HTTP_PATHS = ["/mcp", "/v1/protocol/mcp"];
555
555
 
@@ -753,7 +753,7 @@ var McpAuthGuard = class {
753
753
  return auth?.kind === "org_api_key";
754
754
  }
755
755
  static findTargetOrgIdForTool(name, args) {
756
- 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") {
756
+ 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") {
757
757
  return asString(args.org_id);
758
758
  }
759
759
  return null;
@@ -762,7 +762,7 @@ var McpAuthGuard = class {
762
762
  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") {
763
763
  return ["decision:run", "decision:*", "org:*"];
764
764
  }
765
- 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") {
765
+ 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") {
766
766
  return ["decision:read", "org:read", "decision:*", "org:*"];
767
767
  }
768
768
  if (name === "decionis.upsert_enforcement_rollout" || name === "upsert_enforcement_rollout") {
@@ -859,6 +859,539 @@ function authorizeResourceRead(auth, uri) {
859
859
  // ../protocol/src/mcp/McpRoutes.ts
860
860
  import { randomUUID as randomUUID2 } from "crypto";
861
861
 
862
+ // ../../packages/shared/src/Connectors/ConnectorRegistry.ts
863
+ var SITE = "https://decionis.com";
864
+ var connectors = [
865
+ // ── Commerce ──────────────────────────────────────────────────────────
866
+ {
867
+ // Live Shopify App Store listing (sweep 2026-08-10, also pinned by
868
+ // apps/web ShopifyAppStore.listingUrl + the distributionSameAs sweep).
869
+ // Copy scoped to the published listing description.
870
+ slug: "shopify",
871
+ name: "Decionis for Shopify",
872
+ vendor: "Shopify",
873
+ category: "commerce",
874
+ status: "production",
875
+ 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.",
876
+ capabilities: [
877
+ "margin-floor-enforcement",
878
+ "checkout-policy-enforcement",
879
+ "signed-decision-dossier"
880
+ ],
881
+ decisionsSupported: ["refund", "discount", "oversell", "risky-order"],
882
+ installation: [
883
+ {
884
+ kind: "marketplace",
885
+ url: "https://apps.shopify.com/decionis-checkout-gate",
886
+ label: "Shopify App Store"
887
+ }
888
+ ],
889
+ // Shopify predates the marketplace-target program (it is the original
890
+ // commerce wedge with its own app workspace), so it deliberately has no
891
+ // `marketplaceTarget` join — /commerce is its owning page.
892
+ documentation: `${SITE}/commerce`,
893
+ distribution: { download: false, guidedInstall: true, remoteInstall: false },
894
+ activation: { method: "marketplace_install", requiresWorkspace: true },
895
+ presenceSupport: "planned",
896
+ marketplaceListingId: "shopify-app",
897
+ providerId: "SHOPIFY"
898
+ },
899
+ {
900
+ // Published in the WordPress.org plugin directory (slug `decionis`,
901
+ // 1.0.0) — sweep 2026-08-10 via the distributionSameAs verification.
902
+ // Copy reuses the shared readiness provenance: verified end to end on a
903
+ // staging store before the listing went live; shadow first,
904
+ // enforcement opt-in.
905
+ slug: "woo",
906
+ name: "Decionis for WooCommerce",
907
+ vendor: "WooCommerce",
908
+ category: "commerce",
909
+ status: "production",
910
+ version: "1.0.0",
911
+ 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.",
912
+ capabilities: [
913
+ "margin-floor-enforcement",
914
+ "checkout-policy-enforcement",
915
+ "shadow-mode",
916
+ "signed-decision-dossier"
917
+ ],
918
+ decisionsSupported: ["checkout-order"],
919
+ installation: [
920
+ {
921
+ kind: "marketplace",
922
+ url: "https://wordpress.org/plugins/decionis/",
923
+ label: "WordPress.org Plugin Directory"
924
+ }
925
+ ],
926
+ documentation: `${SITE}/marketplace/woo`,
927
+ distribution: { download: false, guidedInstall: true, remoteInstall: false },
928
+ activation: { method: "api_key", requiresWorkspace: true },
929
+ presenceSupport: "planned",
930
+ marketplaceTarget: "woo",
931
+ marketplaceListingId: "woo-plugin",
932
+ providerId: "WOO"
933
+ },
934
+ {
935
+ // eBay approved the app for production and the connector is deployed and
936
+ // marketplace-account-deletion compliant; the first read-only Fulfillment
937
+ // probe passed against a live seller account (docs/marketplace/ebay/
938
+ // evidence/latest.json). No public eBay listing directory exists, so the
939
+ // channel is the guided OAuth connect — and enforcement stays honest:
940
+ // shadow dossiers today, write-scope approval pending.
941
+ slug: "ebay",
942
+ name: "Decionis for eBay",
943
+ vendor: "eBay",
944
+ category: "commerce",
945
+ status: "installable",
946
+ 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.",
947
+ capabilities: ["margin-guardrails", "promo-hygiene", "shadow-mode", "signed-decision-dossier"],
948
+ decisionsSupported: ["listing-change", "order-fulfillment", "promotion"],
949
+ installation: [
950
+ {
951
+ kind: "guided",
952
+ url: `${SITE}/marketplace/ebay`,
953
+ label: "eBay OAuth connect (guided)"
954
+ }
955
+ ],
956
+ documentation: `${SITE}/marketplace/ebay`,
957
+ verification: {
958
+ validatedBy: "eBay production approval + Decionis live verification",
959
+ 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).",
960
+ lastValidated: "2026-07-14"
961
+ },
962
+ distribution: { download: false, guidedInstall: true, remoteInstall: false },
963
+ activation: { method: "oauth", requiresWorkspace: true },
964
+ presenceSupport: "planned",
965
+ marketplaceTarget: "ebay",
966
+ providerId: "EBAY"
967
+ },
968
+ {
969
+ // Passed ALL Adobe Commerce Marketplace technical review phases (SID
970
+ // qv4l7d2ak6): automated EQP 2026-07-08, full technical-review
971
+ // confirmation received 2026-08-10 (tested by Adobe on Magento 2.4.9
972
+ // EE + CE, PHP 8.5.5). Marketplace release awaits the remaining
973
+ // marketing review, and the gate has not been validated against a live
974
+ // merchant store — so no channels yet; the customer-deployable kit
975
+ // ships next (docs/claims DECIONIS-CLM-022).
976
+ slug: "adobe-commerce",
977
+ name: "Decionis Connector for Adobe Commerce",
978
+ vendor: "Adobe",
979
+ category: "commerce",
980
+ status: "validated",
981
+ version: "1.0.0",
982
+ 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.",
983
+ capabilities: [
984
+ "margin-floor-enforcement",
985
+ "checkout-policy-enforcement",
986
+ "promo-hygiene",
987
+ "fail-open-checkout-gate",
988
+ "signed-decision-dossier"
989
+ ],
990
+ decisionsSupported: ["checkout-order", "pricing-change", "promotion"],
991
+ installation: [
992
+ {
993
+ // The kit page hosts the reviewed composer module + checksum,
994
+ // manifest, and SBOM — shipped in the same change that makes the URL
995
+ // resolve (rule 1.1 by construction).
996
+ kind: "hosted_download",
997
+ url: `${SITE}/marketplace/adobe-commerce/connector-kit`,
998
+ label: "Connector kit (guided deployment)"
999
+ }
1000
+ ],
1001
+ package: {
1002
+ artifact: "/marketplace/adobe-commerce/connector-kit/decionis-module-checkout-guardrail-1.0.0.zip",
1003
+ sha256Path: "/marketplace/adobe-commerce/connector-kit/decionis-module-checkout-guardrail-1.0.0.sha256.txt",
1004
+ manifestPath: "/marketplace/adobe-commerce/connector-kit/manifest.json",
1005
+ sbomPath: "/marketplace/adobe-commerce/connector-kit/SBOM.json",
1006
+ format: "magento2-module"
1007
+ },
1008
+ documentation: `${SITE}/marketplace/adobe-commerce`,
1009
+ verification: {
1010
+ validatedBy: "Adobe Commerce Marketplace technical review (EQP)",
1011
+ 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.",
1012
+ lastValidated: "2026-08-10"
1013
+ },
1014
+ distribution: { download: true, guidedInstall: false, remoteInstall: false },
1015
+ activation: { method: "enrollment_token", requiresWorkspace: true },
1016
+ presenceSupport: "planned",
1017
+ marketplaceTarget: "adobe-commerce",
1018
+ providerId: "ADOBE_COMMERCE"
1019
+ },
1020
+ {
1021
+ // SP-API connector built and unit-tested; shadow-mode first while live
1022
+ // seller validation completes (homepage Beta tile is the owning copy).
1023
+ slug: "amazon",
1024
+ name: "Decionis for Amazon",
1025
+ vendor: "Amazon",
1026
+ category: "commerce",
1027
+ status: "validated",
1028
+ 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.",
1029
+ capabilities: ["margin-guardrails", "shadow-mode", "signed-decision-dossier"],
1030
+ decisionsSupported: ["listing-change", "order"],
1031
+ installation: [],
1032
+ documentation: `${SITE}/marketplace/amazon`,
1033
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
1034
+ activation: { method: "oauth", requiresWorkspace: true },
1035
+ presenceSupport: "planned",
1036
+ marketplaceTarget: "amazon",
1037
+ providerId: "AMAZON"
1038
+ },
1039
+ {
1040
+ // Post-purchase fulfillment circuit breaker — OAuth + refresh, signed
1041
+ // Open API, webhook intake/verification, margin gate, Ship-Package
1042
+ // release built and unit-tested; TikTok App Store review not submitted
1043
+ // (shared readiness provenance).
1044
+ slug: "tiktok",
1045
+ name: "Decionis for TikTok Shop",
1046
+ vendor: "TikTok Shop",
1047
+ category: "commerce",
1048
+ status: "validated",
1049
+ 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.",
1050
+ capabilities: ["fulfillment-circuit-breaker", "margin-guardrails", "signed-decision-dossier"],
1051
+ decisionsSupported: ["order-fulfillment"],
1052
+ installation: [],
1053
+ documentation: `${SITE}/marketplace/tiktok`,
1054
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
1055
+ activation: { method: "oauth", requiresWorkspace: true },
1056
+ presenceSupport: "planned",
1057
+ marketplaceTarget: "tiktok",
1058
+ providerId: "TIKTOK_SHOP"
1059
+ },
1060
+ {
1061
+ // Solution Provider OAuth, PO-created webhook intake with HMAC
1062
+ // verification, margin gate, Acknowledge-Order release/hold — built and
1063
+ // unit-tested; Solution Provider approval and live seller verification
1064
+ // remain (shared readiness provenance).
1065
+ slug: "walmart",
1066
+ name: "Decionis for Walmart Marketplace",
1067
+ vendor: "Walmart",
1068
+ category: "commerce",
1069
+ status: "validated",
1070
+ summary: "Post-purchase fulfillment circuit breaker for Walmart Marketplace \u2014 purchase orders clear a margin gate (COGS + referral fee + shipping) before Acknowledge-Order release.",
1071
+ capabilities: ["fulfillment-circuit-breaker", "margin-guardrails", "signed-decision-dossier"],
1072
+ decisionsSupported: ["order-acknowledgement"],
1073
+ installation: [],
1074
+ documentation: `${SITE}/marketplace/walmart`,
1075
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
1076
+ activation: { method: "oauth", requiresWorkspace: true },
1077
+ presenceSupport: "planned",
1078
+ marketplaceTarget: "walmart",
1079
+ providerId: "WALMART_MARKETPLACE"
1080
+ },
1081
+ {
1082
+ // Vendor Center GOP integration — centralized Mastershop OAuth, durable
1083
+ // order polling, multi-market margin/COD/SLA evaluation, guarded
1084
+ // fulfillment; live Vendor Center verification still required before
1085
+ // enforcement (shared readiness provenance).
1086
+ slug: "jumia",
1087
+ name: "Decionis for Jumia",
1088
+ vendor: "Jumia",
1089
+ category: "commerce",
1090
+ status: "validated",
1091
+ 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.",
1092
+ capabilities: ["fulfillment-circuit-breaker", "margin-guardrails", "signed-decision-dossier"],
1093
+ decisionsSupported: ["order-fulfillment"],
1094
+ installation: [],
1095
+ documentation: `${SITE}/marketplace/jumia`,
1096
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
1097
+ activation: { method: "oauth", requiresWorkspace: true },
1098
+ presenceSupport: "planned",
1099
+ marketplaceTarget: "jumia",
1100
+ providerId: "JUMIA"
1101
+ },
1102
+ {
1103
+ // Headless / Open Checkout gate built and unit-tested; not live-store
1104
+ // validated (rotated off the homepage tiles in favour of WooCommerce).
1105
+ slug: "bigcommerce",
1106
+ name: "Decionis for BigCommerce",
1107
+ vendor: "BigCommerce",
1108
+ category: "commerce",
1109
+ status: "validated",
1110
+ summary: "Checkout margin gate for BigCommerce headless and Open Checkout storefronts \u2014 built and unit-tested, pending validation against a live store.",
1111
+ capabilities: ["margin-floor-enforcement", "checkout-policy-enforcement"],
1112
+ decisionsSupported: ["checkout-order"],
1113
+ installation: [],
1114
+ documentation: `${SITE}/marketplace/bigcommerce`,
1115
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
1116
+ activation: { method: "api_key", requiresWorkspace: true },
1117
+ presenceSupport: "planned",
1118
+ marketplaceTarget: "bigcommerce"
1119
+ },
1120
+ {
1121
+ // Shopware 6 App System handshake, shop-signed webhooks, fail-open
1122
+ // synchronous checkout gate — built and unit-tested, not yet validated
1123
+ // against a live Shopware store (docs/claims DECIONIS-CLM-023).
1124
+ slug: "shopware",
1125
+ name: "Decionis for Shopware",
1126
+ vendor: "Shopware",
1127
+ category: "commerce",
1128
+ status: "validated",
1129
+ 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.",
1130
+ capabilities: [
1131
+ "margin-floor-enforcement",
1132
+ "checkout-policy-enforcement",
1133
+ "promo-hygiene",
1134
+ "fail-open-checkout-gate"
1135
+ ],
1136
+ decisionsSupported: ["checkout-order", "promotion"],
1137
+ installation: [],
1138
+ documentation: `${SITE}/marketplace/shopware`,
1139
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
1140
+ activation: { method: "api_key", requiresWorkspace: true },
1141
+ presenceSupport: "planned",
1142
+ marketplaceTarget: "shopware"
1143
+ },
1144
+ // ── Enterprise ────────────────────────────────────────────────────────
1145
+ {
1146
+ // SAP PartnerEdge Solution Review approved 2026-06-22 (solution id
1147
+ // 2004368, docs/marketplace/sap/solution-hub-listing.md). The PartnerEdge
1148
+ // solution URL is partner-gated (anonymous fetch 404s — sweep
1149
+ // 2026-08-10), so it is deliberately NOT an install channel here; the
1150
+ // customer-deployable BTP deployment kit ships next. Dual-gate wording
1151
+ // from the owning listing copy.
1152
+ slug: "sap",
1153
+ name: "Decionis Governed Execution Gate for SAP",
1154
+ vendor: "SAP",
1155
+ category: "enterprise",
1156
+ status: "validated",
1157
+ version: "0.1.0",
1158
+ 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.",
1159
+ capabilities: [
1160
+ "dual-gate-execution",
1161
+ "segregation-of-duties",
1162
+ "three-way-match",
1163
+ "s4hana-write-back",
1164
+ "signed-decision-dossier"
1165
+ ],
1166
+ decisionsSupported: [
1167
+ "vendor-payment-release",
1168
+ "procurement-approval",
1169
+ "finance-control-change"
1170
+ ],
1171
+ installation: [
1172
+ {
1173
+ // Customer-deployed BTP kit (curated CAP source, mbt build →
1174
+ // cf deploy) — shipped in the same change that makes the URL resolve
1175
+ // (rule 1.1 by construction).
1176
+ kind: "hosted_download",
1177
+ url: `${SITE}/marketplace/sap/deployment-kit`,
1178
+ label: "SAP BTP deployment kit (customer-deployed)"
1179
+ }
1180
+ ],
1181
+ package: {
1182
+ artifact: "/marketplace/sap/deployment-kit/decionis-sap-deployment-kit-0.1.0.zip",
1183
+ sha256Path: "/marketplace/sap/deployment-kit/decionis-sap-deployment-kit-0.1.0.sha256.txt",
1184
+ manifestPath: "/marketplace/sap/deployment-kit/manifest.json",
1185
+ sbomPath: "/marketplace/sap/deployment-kit/SBOM.json",
1186
+ format: "sap-mta-deployment-kit"
1187
+ },
1188
+ documentation: `${SITE}/marketplace/sap`,
1189
+ verification: {
1190
+ validatedBy: "SAP PartnerEdge Solution Review",
1191
+ evidence: "Solution Review completed and approved 2026-06-22 (SAP PartnerEdge solution id 2004368); SAP Store content listing is the next, contract-gated step.",
1192
+ lastValidated: "2026-06-22"
1193
+ },
1194
+ distribution: { download: true, guidedInstall: false, remoteInstall: false },
1195
+ activation: { method: "enrollment_token", requiresWorkspace: true },
1196
+ presenceSupport: "planned",
1197
+ providerId: "SAP"
1198
+ },
1199
+ {
1200
+ // Scoped app x_dec_decionis (tables, script includes, Flow Designer
1201
+ // verify-intent action, shadow observers, workspace dossier panel,
1202
+ // scoped REST API). Self-attested platform evidence — tenant mapping,
1203
+ // ACLs, live policy-check execution, workflow trigger routing — is
1204
+ // recorded in the packaged evidence exports (docs/marketplace/servicenow
1205
+ // submission checklist, evidence refreshed as of the 2026-08-10 sweep).
1206
+ // Store publication deliberately deferred. The customer-deploy kit
1207
+ // CONTENT exists (full-app update-set XML + coverage manifest + INSTALL
1208
+ // under docs/marketplace/servicenow/connector-kit), but the public
1209
+ // download channel + `installable` flip are gated on a real
1210
+ // dev-instance import validation — a user-owned step; never claim the
1211
+ // install before it passes (rule 1.1 on the install claim itself).
1212
+ slug: "servicenow",
1213
+ name: "Decionis Connector for ServiceNow",
1214
+ vendor: "ServiceNow",
1215
+ category: "enterprise",
1216
+ status: "validated",
1217
+ 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.",
1218
+ capabilities: [
1219
+ "flow-designer-verify-intent",
1220
+ "shadow-mode",
1221
+ "decision-drift-evidence",
1222
+ "signed-decision-dossier"
1223
+ ],
1224
+ decisionsSupported: ["itsm-change", "incident-action", "risk-case-action"],
1225
+ installation: [],
1226
+ documentation: `${SITE}/marketplace/servicenow`,
1227
+ verification: {
1228
+ validatedBy: "Decionis platform evidence (self-attested)",
1229
+ 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).",
1230
+ lastValidated: "2026-08-10"
1231
+ },
1232
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
1233
+ activation: { method: "enrollment_token", requiresWorkspace: true },
1234
+ presenceSupport: "planned",
1235
+ marketplaceTarget: "servicenow",
1236
+ providerId: "SERVICENOW"
1237
+ },
1238
+ {
1239
+ // Customer-deployed starter package (Apex Action Gate, LWC, platform
1240
+ // event, permission set) served as committed zips with checksums;
1241
+ // AppExchange Security Review in progress, so no marketplace channel is
1242
+ // claimed (homepage Beta tile is the owning copy).
1243
+ slug: "salesforce",
1244
+ name: "Decionis Action Gate for Salesforce",
1245
+ vendor: "Salesforce",
1246
+ category: "enterprise",
1247
+ status: "installable",
1248
+ 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.",
1249
+ capabilities: [
1250
+ "pre-execution-action-gate",
1251
+ "shadow-mode",
1252
+ "agentforce-governance",
1253
+ "signed-decision-dossier"
1254
+ ],
1255
+ decisionsSupported: ["flow-action", "apex-action", "agentforce-action"],
1256
+ installation: [
1257
+ {
1258
+ kind: "hosted_download",
1259
+ url: `${SITE}/marketplace/salesforce/starter-package`,
1260
+ label: "Salesforce starter package (customer-deployed)"
1261
+ }
1262
+ ],
1263
+ package: {
1264
+ artifact: "/marketplace/salesforce/starter-package/decionis-salesforce-starter-package.zip",
1265
+ sha256Path: "/marketplace/salesforce/starter-package/decionis-salesforce-starter-package.sha256.txt",
1266
+ format: "salesforce-metadata-source"
1267
+ },
1268
+ documentation: `${SITE}/marketplace/salesforce`,
1269
+ distribution: { download: true, guidedInstall: false, remoteInstall: false },
1270
+ activation: { method: "api_key", requiresWorkspace: true },
1271
+ presenceSupport: "planned",
1272
+ marketplaceTarget: "salesforce",
1273
+ marketplaceListingId: "salesforce-app",
1274
+ providerId: "SALESFORCE"
1275
+ },
1276
+ // ── Automation ────────────────────────────────────────────────────────
1277
+ {
1278
+ // Live Zapier app-directory listing (sweep 2026-08-10: page resolves and
1279
+ // shows the Decionis app with the Create Decision Dossier action).
1280
+ slug: "zapier",
1281
+ name: "Decionis for Zapier",
1282
+ vendor: "Zapier",
1283
+ category: "automation",
1284
+ status: "production",
1285
+ 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.",
1286
+ capabilities: ["automation-step-governance", "shadow-mode", "signed-decision-dossier"],
1287
+ decisionsSupported: ["automation-step"],
1288
+ installation: [
1289
+ {
1290
+ kind: "marketplace",
1291
+ url: "https://zapier.com/apps/decionis/integrations",
1292
+ label: "Zapier app directory"
1293
+ }
1294
+ ],
1295
+ documentation: `${SITE}/docs/zapier`,
1296
+ distribution: { download: false, guidedInstall: true, remoteInstall: false },
1297
+ activation: { method: "oauth", requiresWorkspace: true },
1298
+ presenceSupport: "planned",
1299
+ marketplaceListingId: "zapier"
1300
+ },
1301
+ {
1302
+ // The native Make app is built with its OAuth on-ramp live, but the Make
1303
+ // listing is private in review — so the installable path today is the
1304
+ // documented HTTP recipe (AutomationConnectors owning copy, status
1305
+ // `preview`).
1306
+ slug: "make",
1307
+ name: "Decionis for Make",
1308
+ vendor: "Make",
1309
+ category: "automation",
1310
+ status: "installable",
1311
+ 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.",
1312
+ capabilities: ["automation-step-governance", "signed-decision-dossier"],
1313
+ decisionsSupported: ["automation-step"],
1314
+ installation: [
1315
+ {
1316
+ kind: "docs",
1317
+ url: `${SITE}/docs/automation-connectors#make`,
1318
+ label: "HTTP recipe (native app listing in review)"
1319
+ }
1320
+ ],
1321
+ documentation: `${SITE}/docs/automation-connectors#make`,
1322
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
1323
+ activation: { method: "oauth", requiresWorkspace: true },
1324
+ presenceSupport: "planned"
1325
+ },
1326
+ {
1327
+ // Community node published on npm (registry sweep 2026-08-10:
1328
+ // n8n-nodes-decionis@0.1.0).
1329
+ slug: "n8n",
1330
+ name: "Decionis for n8n",
1331
+ vendor: "n8n",
1332
+ category: "automation",
1333
+ status: "production",
1334
+ version: "0.1.0",
1335
+ 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.",
1336
+ capabilities: ["automation-step-governance", "signed-decision-dossier"],
1337
+ decisionsSupported: ["automation-step"],
1338
+ installation: [
1339
+ {
1340
+ kind: "npm",
1341
+ url: "https://www.npmjs.com/package/n8n-nodes-decionis",
1342
+ label: "n8n community node on npm"
1343
+ }
1344
+ ],
1345
+ documentation: `${SITE}/docs/automation-connectors#n8n`,
1346
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
1347
+ activation: { method: "api_key", requiresWorkspace: true },
1348
+ presenceSupport: "planned"
1349
+ },
1350
+ // ── Platform ──────────────────────────────────────────────────────────
1351
+ {
1352
+ // The standard webhook path into the protocol — the same Gate 1
1353
+ // admission check, any backend (owning copy: ERP & Webhooks tile +
1354
+ // /docs/webhooks).
1355
+ // `installable`, not `production`: the webhook path is live product
1356
+ // capability, but there is no public marketplace/registry listing to
1357
+ // install it from — you wire it from the documented recipe.
1358
+ slug: "webhooks",
1359
+ name: "ERP & Webhooks",
1360
+ vendor: "Any system of record",
1361
+ category: "platform",
1362
+ status: "installable",
1363
+ summary: "Cross-verify cost basis and inventory against your system of record via standard webhooks \u2014 the same Gate 1 admission check, any backend.",
1364
+ capabilities: ["erp-cross-verification", "webhook-intake"],
1365
+ decisionsSupported: ["cost-basis-verification", "inventory-verification"],
1366
+ installation: [
1367
+ { kind: "docs", url: `${SITE}/docs/webhooks`, label: "Webhook integration guide" }
1368
+ ],
1369
+ documentation: `${SITE}/docs/webhooks`,
1370
+ distribution: { download: false, guidedInstall: false, remoteInstall: false },
1371
+ activation: { method: "api_key", requiresWorkspace: true },
1372
+ presenceSupport: "planned",
1373
+ providerId: "WEBHOOK"
1374
+ }
1375
+ ];
1376
+ var ConnectorRegistry = class {
1377
+ static connectors = connectors;
1378
+ static bySlug(slug) {
1379
+ return connectors.find((connector) => connector.slug === slug);
1380
+ }
1381
+ static byCategory(category) {
1382
+ return connectors.filter((connector) => connector.category === category);
1383
+ }
1384
+ static byStatus(status) {
1385
+ return connectors.filter((connector) => connector.status === status);
1386
+ }
1387
+ static slugs() {
1388
+ return connectors.map((connector) => connector.slug);
1389
+ }
1390
+ };
1391
+ var CONNECTOR_REGISTRY = ConnectorRegistry.connectors;
1392
+ var getConnector = ConnectorRegistry.bySlug;
1393
+ var CONNECTOR_SLUGS = ConnectorRegistry.slugs();
1394
+
862
1395
  // ../protocol/src/mcp/McpResources.ts
863
1396
  var McpResources = class {
864
1397
  static buildStaticResources(publicBaseUrl, serviceName, serviceVersion) {
@@ -898,6 +1431,13 @@ var McpResources = class {
898
1431
  description: "Available vertical packs and workflow overlays exposed by Decionis Protocol.",
899
1432
  mimeType: "application/json"
900
1433
  },
1434
+ {
1435
+ uri: "decionis://connectors/registry",
1436
+ name: "connector_registry",
1437
+ title: "Decionis Connector Registry",
1438
+ description: "Every Decionis connector with availability status, install channels, governed actions, and verification evidence \u2014 the same registry that generates decionis.com/integrations.",
1439
+ mimeType: "application/json"
1440
+ },
901
1441
  {
902
1442
  uri: "decionis://protocol/webex-mcp-registration",
903
1443
  name: "webex_mcp_registration",
@@ -1120,7 +1660,8 @@ var McpResources = class {
1120
1660
  "resources/read -> decionis://protocol/webex-agent-intent-contract",
1121
1661
  "resources/read -> decionis://protocol/webex-reviewer-scenarios",
1122
1662
  "tools/call -> decionis.evaluate_webex_agent_intent",
1123
- "tools/call -> decionis.get_decision_dossier"
1663
+ "tools/call -> decionis.get_decision_dossier",
1664
+ "tools/call -> decionis.get_decision_chain"
1124
1665
  ]
1125
1666
  }
1126
1667
  };
@@ -1146,10 +1687,34 @@ var McpResources = class {
1146
1687
  });
1147
1688
  return { mimeType: "application/json", value: response.body };
1148
1689
  }
1690
+ if (uri === "decionis://connectors/registry") {
1691
+ return {
1692
+ mimeType: "application/json",
1693
+ value: {
1694
+ registry: "decionis-connectors",
1695
+ statuses: {
1696
+ production: "generally available today through a live public channel",
1697
+ installable: "customer-deployable today outside public marketplaces",
1698
+ validated: "built and verified (see each connector's verification); downloadable when a package is listed",
1699
+ planned: "roadmap; never carries install channels"
1700
+ },
1701
+ connectors: CONNECTOR_REGISTRY,
1702
+ human_catalogue: "https://decionis.com/integrations"
1703
+ }
1704
+ };
1705
+ }
1149
1706
  const parsed = new URL(uri);
1150
1707
  if (parsed.protocol !== "decionis:") {
1151
1708
  throw new Error(`Unsupported resource URI '${uri}'`);
1152
1709
  }
1710
+ if (parsed.hostname === "connectors") {
1711
+ const slug = decodeURIComponent(parsed.pathname.replace(/^\//, ""));
1712
+ const connector = getConnector(slug);
1713
+ if (!connector) {
1714
+ throw new Error(`Unknown connector '${slug}'`);
1715
+ }
1716
+ return { mimeType: "application/json", value: connector };
1717
+ }
1153
1718
  if (parsed.hostname === "marketplaces" && parsed.pathname.startsWith("/targets/")) {
1154
1719
  const target = decodeURIComponent(parsed.pathname.replace("/targets/", ""));
1155
1720
  const response = await invokeProtocolRoute(app, {
@@ -1419,7 +1984,7 @@ var McpTools = class {
1419
1984
  return cart && typeof cart.amount === "number" ? cart.amount : void 0;
1420
1985
  }
1421
1986
  static buildTools(app) {
1422
- return [
1987
+ const tools = [
1423
1988
  {
1424
1989
  name: "decionis.evaluate_decision",
1425
1990
  aliases: ["evaluate_decision"],
@@ -1675,6 +2240,37 @@ var McpTools = class {
1675
2240
  return response.body;
1676
2241
  }
1677
2242
  },
2243
+ {
2244
+ name: "decionis.get_decision_chain",
2245
+ aliases: ["get_decision_chain"],
2246
+ title: "Get Decision Chain",
2247
+ 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.",
2248
+ inputSchema: {
2249
+ type: "object",
2250
+ required: ["org_id", "chain_id"],
2251
+ additionalProperties: false,
2252
+ properties: {
2253
+ org_id: { type: "string", description: "Org UUID." },
2254
+ chain_id: { type: "string", description: "Decision chain UUID." }
2255
+ }
2256
+ },
2257
+ outputSchema: { type: "object", additionalProperties: true },
2258
+ async handler(args) {
2259
+ const orgId = asString(args.org_id);
2260
+ const chainId = asString(args.chain_id);
2261
+ if (!orgId || !chainId) {
2262
+ throw new Error("org_id and chain_id are required");
2263
+ }
2264
+ const response = await invokeProtocolRoute(app, {
2265
+ method: "GET",
2266
+ url: `/v1/protocol/decision-chains/${encodeURIComponent(chainId)}?org_id=${encodeURIComponent(orgId)}`
2267
+ });
2268
+ if (!response.ok) {
2269
+ throw new Error(`Decision chain lookup failed (${response.statusCode})`);
2270
+ }
2271
+ return response.body;
2272
+ }
2273
+ },
1678
2274
  {
1679
2275
  name: "decionis.list_enforcement_rollouts",
1680
2276
  aliases: ["list_enforcement_rollouts"],
@@ -1893,6 +2489,29 @@ var McpTools = class {
1893
2489
  }
1894
2490
  }
1895
2491
  ];
2492
+ const readOnlyTools = /* @__PURE__ */ new Set([
2493
+ "decionis.get_decision_dossier",
2494
+ "decionis.get_decision_chain",
2495
+ "decionis.list_enforcement_rollouts",
2496
+ "decionis.list_marketplace_targets",
2497
+ "decionis.get_marketplace_target",
2498
+ "decionis.list_marketplace_readiness",
2499
+ "decionis.list_vertical_packs",
2500
+ "decionis.resolve_vertical_pack"
2501
+ ]);
2502
+ const idempotentWriteTools = /* @__PURE__ */ new Set(["decionis.upsert_enforcement_rollout"]);
2503
+ return tools.map((tool) => {
2504
+ const readOnly = readOnlyTools.has(tool.name);
2505
+ return {
2506
+ ...tool,
2507
+ annotations: {
2508
+ readOnlyHint: readOnly,
2509
+ destructiveHint: false,
2510
+ idempotentHint: readOnly || idempotentWriteTools.has(tool.name),
2511
+ openWorldHint: false
2512
+ }
2513
+ };
2514
+ });
1896
2515
  }
1897
2516
  static buildTextToolResult(value, options) {
1898
2517
  const text = toPrettyText(value);
@@ -1946,6 +2565,37 @@ function findTool(tools, name) {
1946
2565
  }
1947
2566
 
1948
2567
  // ../protocol/src/mcp/McpRoutes.ts
2568
+ async function recordTelemetry(app, sink, event) {
2569
+ if (!sink) return;
2570
+ try {
2571
+ await sink.record(event);
2572
+ } catch (err) {
2573
+ app.log.warn({ err, event: event.event }, "MCP telemetry write failed");
2574
+ }
2575
+ }
2576
+ function canonicalAuthorizationOutcome(value) {
2577
+ const record = asRecord(value);
2578
+ const raw = asString(record?.outcome) ?? asString(record?.decision) ?? asString(record?.verdict);
2579
+ if (!raw) return null;
2580
+ switch (raw.trim().toUpperCase()) {
2581
+ case "ALLOW":
2582
+ case "APPROVE":
2583
+ case "AUTO_APPROVE":
2584
+ return "ALLOW";
2585
+ case "BLOCK":
2586
+ case "REJECT":
2587
+ case "AUTO_REJECT":
2588
+ return "BLOCK";
2589
+ case "ESCALATE":
2590
+ return "ESCALATE";
2591
+ case "REVIEW":
2592
+ case "RESTRAIN":
2593
+ case "REQUIRE_REVIEW":
2594
+ return "REVIEW";
2595
+ default:
2596
+ return null;
2597
+ }
2598
+ }
1949
2599
  function buildInitializeResult(serviceName, serviceVersion) {
1950
2600
  return {
1951
2601
  protocolVersion: MCP_DEFAULT_PROTOCOL_VERSION,
@@ -1959,7 +2609,7 @@ function buildInitializeResult(serviceName, serviceVersion) {
1959
2609
  title: "Decionis Protocol MCP",
1960
2610
  version: serviceVersion
1961
2611
  },
1962
- 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."
2612
+ 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."
1963
2613
  };
1964
2614
  }
1965
2615
  async function handleJsonRpcRequest(app, requestBody, options) {
@@ -1997,6 +2647,16 @@ async function handleJsonRpcRequest(app, requestBody, options) {
1997
2647
  }
1998
2648
  const requestedVersion = asString(params.protocolVersion);
1999
2649
  const negotiatedVersion = requestedVersion && MCP_PROTOCOL_VERSIONS.includes(requestedVersion) ? requestedVersion : MCP_DEFAULT_PROTOCOL_VERSION;
2650
+ const clientInfo = asRecord(params.clientInfo);
2651
+ await recordTelemetry(app, options.telemetry, {
2652
+ event: "connection_succeeded",
2653
+ connectionId: options.connectionId,
2654
+ clientName: asString(clientInfo?.name),
2655
+ clientVersion: asString(clientInfo?.version),
2656
+ protocolVersion: negotiatedVersion,
2657
+ authMode: options.auth?.kind ?? "none",
2658
+ statusCode: 200
2659
+ });
2000
2660
  return {
2001
2661
  statusCode: 200,
2002
2662
  payload: jsonRpcSuccess(id, {
@@ -2012,6 +2672,12 @@ async function handleJsonRpcRequest(app, requestBody, options) {
2012
2672
  case "ping":
2013
2673
  return { statusCode: 200, payload: jsonRpcSuccess(id, {}) };
2014
2674
  case "tools/list":
2675
+ await recordTelemetry(app, options.telemetry, {
2676
+ event: "tools_listed",
2677
+ connectionId: options.connectionId,
2678
+ authMode: options.auth?.kind ?? "none",
2679
+ statusCode: 200
2680
+ });
2015
2681
  return {
2016
2682
  statusCode: 200,
2017
2683
  payload: jsonRpcSuccess(id, {
@@ -2020,7 +2686,8 @@ async function handleJsonRpcRequest(app, requestBody, options) {
2020
2686
  title: tool.title,
2021
2687
  description: tool.description,
2022
2688
  inputSchema: tool.inputSchema,
2023
- ...tool.outputSchema ? { outputSchema: tool.outputSchema } : {}
2689
+ ...tool.outputSchema ? { outputSchema: tool.outputSchema } : {},
2690
+ annotations: tool.annotations
2024
2691
  }))
2025
2692
  })
2026
2693
  };
@@ -2039,15 +2706,61 @@ async function handleJsonRpcRequest(app, requestBody, options) {
2039
2706
  payload: jsonRpcError(id, -32601, `Unknown tool '${name}'`)
2040
2707
  };
2041
2708
  }
2709
+ const toolStartedAt = process.hrtime.bigint();
2710
+ await recordTelemetry(app, options.telemetry, {
2711
+ event: "tool_called",
2712
+ connectionId: options.connectionId,
2713
+ toolName: tool.name,
2714
+ authMode: options.auth?.kind ?? "none"
2715
+ });
2042
2716
  const args = asRecord(params.arguments) ?? {};
2043
2717
  const authError = authorizeToolInvocation(options.auth, name, args);
2044
2718
  if (authError) {
2719
+ await recordTelemetry(app, options.telemetry, {
2720
+ event: "authorization_outcome",
2721
+ connectionId: options.connectionId,
2722
+ toolName: tool.name,
2723
+ outcome: "DENIED",
2724
+ authMode: options.auth?.kind ?? "none",
2725
+ statusCode: 403,
2726
+ durationMs: durationMs(toolStartedAt)
2727
+ });
2728
+ await recordTelemetry(app, options.telemetry, {
2729
+ event: "tool_failed",
2730
+ connectionId: options.connectionId,
2731
+ toolName: tool.name,
2732
+ outcome: "DENIED",
2733
+ authMode: options.auth?.kind ?? "none",
2734
+ statusCode: 403,
2735
+ durationMs: durationMs(toolStartedAt)
2736
+ });
2045
2737
  return {
2046
2738
  statusCode: 403,
2047
2739
  payload: jsonRpcError(id, authError.code, authError.message)
2048
2740
  };
2049
2741
  }
2050
2742
  const result = await handleToolCall(tool, args);
2743
+ const outcome = canonicalAuthorizationOutcome(result.structuredContent);
2744
+ await recordTelemetry(app, options.telemetry, {
2745
+ event: result.isError ? "tool_failed" : "tool_succeeded",
2746
+ connectionId: options.connectionId,
2747
+ toolName: tool.name,
2748
+ outcome,
2749
+ authMode: options.auth?.kind ?? "none",
2750
+ statusCode: 200,
2751
+ durationMs: durationMs(toolStartedAt)
2752
+ });
2753
+ if (outcome) {
2754
+ await recordTelemetry(app, options.telemetry, {
2755
+ event: "authorization_outcome",
2756
+ connectionId: options.connectionId,
2757
+ toolName: tool.name,
2758
+ outcome,
2759
+ authMode: options.auth?.kind ?? "none",
2760
+ statusCode: 200,
2761
+ durationMs: durationMs(toolStartedAt)
2762
+ });
2763
+ }
2051
2764
  return {
2052
2765
  statusCode: 200,
2053
2766
  payload: jsonRpcSuccess(id, result)
@@ -2063,6 +2776,13 @@ async function handleJsonRpcRequest(app, requestBody, options) {
2063
2776
  statusCode: 200,
2064
2777
  payload: jsonRpcSuccess(id, {
2065
2778
  resourceTemplates: [
2779
+ {
2780
+ uriTemplate: "decionis://connectors/{slug}",
2781
+ name: "connector_detail",
2782
+ title: "Connector Detail",
2783
+ description: "Read one Connector Registry row: status, install channels, governed actions, verification.",
2784
+ mimeType: "application/json"
2785
+ },
2066
2786
  {
2067
2787
  uriTemplate: "decionis://marketplaces/targets/{target}",
2068
2788
  name: "marketplace_target_detail",
@@ -2224,6 +2944,11 @@ async function handleMcpHttpRequest(app, req, reply, options) {
2224
2944
  const auth = await authenticateMcpRequest(req, authLookup);
2225
2945
  const authDurationMs = durationMs(authStartedAt);
2226
2946
  if (!auth && isAuthenticationRequired(authLookup)) {
2947
+ await recordTelemetry(app, options.telemetry ?? null, {
2948
+ event: "authentication_failed",
2949
+ statusCode: 401,
2950
+ durationMs: authDurationMs
2951
+ });
2227
2952
  const maybeId = asRecord(req.body)?.id;
2228
2953
  const id = typeof maybeId === "string" || typeof maybeId === "number" ? maybeId : null;
2229
2954
  reply.header(
@@ -2233,6 +2958,10 @@ async function handleMcpHttpRequest(app, req, reply, options) {
2233
2958
  stages: { auth_ms: authDurationMs }
2234
2959
  })
2235
2960
  );
2961
+ reply.header(
2962
+ "www-authenticate",
2963
+ `Bearer resource_metadata="${options.publicBaseUrl}/.well-known/oauth-protected-resource", error="invalid_token"`
2964
+ );
2236
2965
  return reply.code(401).send(jsonRpcError(id, -32001, "Unauthorized MCP request"));
2237
2966
  }
2238
2967
  if (Array.isArray(req.body)) {
@@ -2247,7 +2976,35 @@ async function handleMcpHttpRequest(app, req, reply, options) {
2247
2976
  }
2248
2977
  if (isLegacyToolCall(req.body)) {
2249
2978
  const rpcStartedAt2 = process.hrtime.bigint();
2979
+ const connectionId2 = asString(req.headers["mcp-session-id"]);
2980
+ await recordTelemetry(app, options.telemetry ?? null, {
2981
+ event: "tool_called",
2982
+ connectionId: connectionId2,
2983
+ toolName: req.body.tool_name,
2984
+ authMode: auth?.kind ?? "none"
2985
+ });
2250
2986
  const payload = await handleLegacyToolCall(app, auth, req.body);
2987
+ const outcome = canonicalAuthorizationOutcome(payload.output);
2988
+ await recordTelemetry(app, options.telemetry ?? null, {
2989
+ event: payload.is_error || payload.error ? "tool_failed" : "tool_succeeded",
2990
+ connectionId: connectionId2,
2991
+ toolName: req.body.tool_name,
2992
+ outcome,
2993
+ authMode: auth?.kind ?? "none",
2994
+ statusCode: 200,
2995
+ durationMs: durationMs(rpcStartedAt2)
2996
+ });
2997
+ if (outcome) {
2998
+ await recordTelemetry(app, options.telemetry ?? null, {
2999
+ event: "authorization_outcome",
3000
+ connectionId: connectionId2,
3001
+ toolName: req.body.tool_name,
3002
+ outcome,
3003
+ authMode: auth?.kind ?? "none",
3004
+ statusCode: 200,
3005
+ durationMs: durationMs(rpcStartedAt2)
3006
+ });
3007
+ }
2251
3008
  reply.header(
2252
3009
  "server-timing",
2253
3010
  buildServerTimingHeader({
@@ -2272,13 +3029,21 @@ async function handleMcpHttpRequest(app, req, reply, options) {
2272
3029
  return reply.code(400).send(jsonRpcError(null, -32600, "JSON-RPC request body required"));
2273
3030
  }
2274
3031
  const rpcStartedAt = process.hrtime.bigint();
3032
+ const requestedMethod = asString(requestBody.method);
3033
+ const incomingSession = asString(req.headers["mcp-session-id"]);
3034
+ const connectionId = requestedMethod === "initialize" ? randomUUID2() : incomingSession;
2275
3035
  const result = await handleJsonRpcRequest(app, requestBody, {
2276
3036
  publicBaseUrl: options.publicBaseUrl,
2277
3037
  serviceName: options.serviceName,
2278
3038
  serviceVersion: options.serviceVersion,
2279
3039
  auth,
2280
- authLookup
3040
+ authLookup,
3041
+ telemetry: options.telemetry ?? null,
3042
+ connectionId
2281
3043
  });
3044
+ if (requestedMethod === "initialize" && result.statusCode === 200) {
3045
+ reply.header("mcp-session-id", connectionId);
3046
+ }
2282
3047
  reply.header(
2283
3048
  "server-timing",
2284
3049
  buildServerTimingHeader({
@@ -2308,8 +3073,21 @@ function registerProtocolMcpRoutes(app, options) {
2308
3073
  endpoint: `${publicBaseUrl}/v1/protocol/mcp`,
2309
3074
  endpoint_aliases: MCP_HTTP_PATHS,
2310
3075
  supported_protocol_versions: [...MCP_PROTOCOL_VERSIONS],
2311
- auth
3076
+ auth,
3077
+ oauth: {
3078
+ protected_resource_metadata: `${publicBaseUrl}/.well-known/oauth-protected-resource`,
3079
+ authorization_server_metadata: "https://api.decionis.com/.well-known/oauth-authorization-server"
3080
+ }
3081
+ };
3082
+ const oauthProtectedResource = {
3083
+ resource: `${publicBaseUrl}/mcp`,
3084
+ authorization_servers: ["https://api.decionis.com"],
3085
+ bearer_methods_supported: ["header"],
3086
+ scopes_supported: ["decision:read", "decision:run", "decision:write"],
3087
+ resource_documentation: "https://decionis.com/docs/protocol-mcp"
2312
3088
  };
3089
+ app.get("/.well-known/oauth-protected-resource", async () => oauthProtectedResource);
3090
+ app.get("/.well-known/oauth-protected-resource/mcp", async () => oauthProtectedResource);
2313
3091
  for (const path of MCP_HTTP_PATHS) {
2314
3092
  app.get(path, async () => metadata);
2315
3093
  app.delete(path, async (_req, reply) => reply.code(204).send());
@@ -2319,7 +3097,8 @@ function registerProtocolMcpRoutes(app, options) {
2319
3097
  publicBaseUrl,
2320
3098
  serviceName: options.serviceName,
2321
3099
  serviceVersion: options.serviceVersion,
2322
- authLookup
3100
+ authLookup,
3101
+ telemetry: options.telemetry ?? null
2323
3102
  })
2324
3103
  );
2325
3104
  }
@@ -10510,7 +11289,8 @@ function buildDecisionDossierPersistedProofBundle(input) {
10510
11289
  }
10511
11290
 
10512
11291
  // ../protocol/src/support/PanDetection.ts
10513
- var PanDetection = class {
11292
+ var PanDetection = class _PanDetection {
11293
+ static UUID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10514
11294
  static toDigits(value) {
10515
11295
  if (typeof value !== "string" && typeof value !== "number") return null;
10516
11296
  const digits = String(value).replace(/\D/g, "");
@@ -10532,12 +11312,32 @@ var PanDetection = class {
10532
11312
  }
10533
11313
  return sum % 10 === 0;
10534
11314
  }
11315
+ /**
11316
+ * Candidate card numbers inside a value: runs of digits separated only by the
11317
+ * characters a card number is actually written with (spaces and dashes).
11318
+ *
11319
+ * Splitting on everything else is the whole point. Stripping every non-digit
11320
+ * from the value instead — which this used to do — welds unrelated digits
11321
+ * together across letters and punctuation, and a UUID is the case that bites:
11322
+ * discard its letters and roughly 4% of them are a 13-to-19-digit string that
11323
+ * satisfies Luhn. Since we put UUIDs into decision context ourselves (the
11324
+ * duplicate-payment fact carries the prior dossier id), that turned a
11325
+ * legitimate duplicate-payment decision into "raw PAN-like value detected"
11326
+ * about one time in twenty-five.
11327
+ */
11328
+ static panCandidates(value) {
11329
+ if (typeof value === "number") return [String(value).replace(/[^\d]/g, "")];
11330
+ if (typeof value !== "string") return [];
11331
+ if (_PanDetection.UUID_SHAPE.test(value.trim())) return [];
11332
+ const runs = value.match(/(?<![0-9A-Za-z])\d[\d \-]*\d(?![0-9A-Za-z])/g) ?? [];
11333
+ return runs.map((run) => run.replace(/[^\d]/g, "")).filter((digits) => digits.length > 0);
11334
+ }
10535
11335
  static looksLikeRawPan(value) {
10536
- const digits = toDigits(value);
10537
- if (!digits) return false;
10538
- if (digits.length < 13 || digits.length > 19) return false;
10539
- if (/^(\d)\1+$/.test(digits)) return false;
10540
- return isLuhnValid(digits);
11336
+ return _PanDetection.panCandidates(value).some((digits) => {
11337
+ if (digits.length < 13 || digits.length > 19) return false;
11338
+ if (/^(\d)\1+$/.test(digits)) return false;
11339
+ return isLuhnValid(digits);
11340
+ });
10541
11341
  }
10542
11342
  static findRawPanPath(value, path, visited = /* @__PURE__ */ new Set()) {
10543
11343
  if (looksLikeRawPan(value)) return path;
@@ -10558,9 +11358,6 @@ var PanDetection = class {
10558
11358
  return null;
10559
11359
  }
10560
11360
  };
10561
- function toDigits(value) {
10562
- return PanDetection.toDigits(value);
10563
- }
10564
11361
  function isLuhnValid(digits) {
10565
11362
  return PanDetection.isLuhnValid(digits);
10566
11363
  }
@@ -15836,10 +16633,15 @@ var readiness = {
15836
16633
  "adobe-commerce": {
15837
16634
  target: "adobe-commerce",
15838
16635
  protocol_capture: "CAPTURED",
16636
+ // adapter_state stays BUILDING deliberately: Adobe's Marketplace
16637
+ // technical review passed in full (all phases, SID qv4l7d2ak6 — automated
16638
+ // EQP 2026-07-08, final confirmation 2026-08-10, tested on Magento 2.4.9
16639
+ // EE + CE / PHP 8.5.5), but the gate has not yet run against a live
16640
+ // merchant store, and that is the bar LIVE means here.
15839
16641
  adapter_state: "BUILDING",
15840
- docs_state: "PLANNED",
16642
+ docs_state: "BUILDING",
15841
16643
  launch_state: "PLANNED",
15842
- next_milestone: "Shadow\u2192enforce control shipped; onboard a live Adobe Commerce store in shadow, verify the checkout gate, then promote it to enforcement"
16644
+ 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"
15843
16645
  },
15844
16646
  shopware: {
15845
16647
  target: "shopware",
@@ -15860,10 +16662,16 @@ var readiness = {
15860
16662
  woo: {
15861
16663
  target: "woo",
15862
16664
  protocol_capture: "CAPTURED",
15863
- adapter_state: "BUILDING",
15864
- docs_state: "PLANNED",
15865
- launch_state: "PLANNED",
15866
- next_milestone: "Verify native authorization plus Block and classic checkout parity against a live WooCommerce store before enabling enforcement"
16665
+ // Truth-up 2026-08-10: the plugin is published in the WordPress.org
16666
+ // directory (slug `decionis`, 1.0.0) and Block + classic checkout,
16667
+ // core COGS, HPOS order CRUD, and the signed dossier were verified end
16668
+ // to end on a staging store — including an admin-enabled ENFORCEMENT
16669
+ // block — before the listing went live (shared readiness provenance).
16670
+ // The previous PLANNED states predated the public listing.
16671
+ adapter_state: "LIVE",
16672
+ docs_state: "LIVE",
16673
+ launch_state: "BUILDING",
16674
+ 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"
15867
16675
  },
15868
16676
  jumia: {
15869
16677
  target: "jumia",
@@ -16565,7 +17373,8 @@ function buildProtocolApp(options) {
16565
17373
  registerProtocolMcpRoutes(app, {
16566
17374
  serviceName: PROTOCOL_SERVICE,
16567
17375
  serviceVersion: PROTOCOL_VERSION,
16568
- authLookup: options?.mcpAuthLookup ?? null
17376
+ authLookup: options?.mcpAuthLookup ?? null,
17377
+ telemetry: options?.mcpTelemetry ?? null
16569
17378
  });
16570
17379
  const executionAuthority = createExecutionGatingService(options?.executionGating);
16571
17380
  registerExecutionGatingRoutes(app, {
@@ -16941,4 +17750,4 @@ export {
16941
17750
  normalizeVerdict,
16942
17751
  PolicyFileRepository
16943
17752
  };
16944
- //# sourceMappingURL=chunk-QSCP7MRQ.js.map
17753
+ //# sourceMappingURL=chunk-KY5AVRL6.js.map