@astrasyncai/verification-gateway 2.4.4 → 2.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +82 -0
  2. package/dist/adapter-interface/interface.d.mts +2 -2
  3. package/dist/adapter-interface/interface.d.ts +2 -2
  4. package/dist/adapters/express.d.mts +2 -2
  5. package/dist/adapters/express.d.ts +2 -2
  6. package/dist/adapters/express.js +4 -0
  7. package/dist/adapters/express.js.map +1 -1
  8. package/dist/adapters/express.mjs +4 -0
  9. package/dist/adapters/express.mjs.map +1 -1
  10. package/dist/adapters/mcp.d.mts +54 -18
  11. package/dist/adapters/mcp.d.ts +54 -18
  12. package/dist/adapters/mcp.js +76 -23
  13. package/dist/adapters/mcp.js.map +1 -1
  14. package/dist/adapters/mcp.mjs +76 -23
  15. package/dist/adapters/mcp.mjs.map +1 -1
  16. package/dist/adapters/nextjs.d.mts +2 -2
  17. package/dist/adapters/nextjs.d.ts +2 -2
  18. package/dist/adapters/nextjs.js +4 -0
  19. package/dist/adapters/nextjs.js.map +1 -1
  20. package/dist/adapters/nextjs.mjs +4 -0
  21. package/dist/adapters/nextjs.mjs.map +1 -1
  22. package/dist/adapters/sdk.d.mts +2 -2
  23. package/dist/adapters/sdk.d.ts +2 -2
  24. package/dist/adapters/sdk.js +4 -0
  25. package/dist/adapters/sdk.js.map +1 -1
  26. package/dist/adapters/sdk.mjs +4 -0
  27. package/dist/adapters/sdk.mjs.map +1 -1
  28. package/dist/agent/index.d.mts +2 -2
  29. package/dist/agent/index.d.ts +2 -2
  30. package/dist/browser/background.js +4 -0
  31. package/dist/browser/background.js.map +1 -1
  32. package/dist/browser/background.mjs +4 -0
  33. package/dist/browser/background.mjs.map +1 -1
  34. package/dist/browser/browser-adapter.d.mts +2 -2
  35. package/dist/browser/browser-adapter.d.ts +2 -2
  36. package/dist/cli/index.d.mts +2 -2
  37. package/dist/cli/index.d.ts +2 -2
  38. package/dist/cursor/cursor-adapter.d.mts +2 -2
  39. package/dist/cursor/cursor-adapter.d.ts +2 -2
  40. package/dist/cursor/extension.d.mts +2 -2
  41. package/dist/cursor/extension.d.ts +2 -2
  42. package/dist/cursor/extension.js +4 -0
  43. package/dist/cursor/extension.js.map +1 -1
  44. package/dist/cursor/extension.mjs +4 -0
  45. package/dist/cursor/extension.mjs.map +1 -1
  46. package/dist/{express-Ck2RHZLT.d.mts → express-D5hAJ2Gv.d.mts} +1 -1
  47. package/dist/{express-DZmEzCgo.d.ts → express-XCkk7BsJ.d.ts} +1 -1
  48. package/dist/gateway/gateway.d.mts +2 -2
  49. package/dist/gateway/gateway.d.ts +2 -2
  50. package/dist/gateway/gateway.js +4 -0
  51. package/dist/gateway/gateway.js.map +1 -1
  52. package/dist/gateway/gateway.mjs +4 -0
  53. package/dist/gateway/gateway.mjs.map +1 -1
  54. package/dist/git-trigger/git-hooks.d.mts +2 -2
  55. package/dist/git-trigger/git-hooks.d.ts +2 -2
  56. package/dist/{index-6Jus6yWU.d.ts → index-Bstl43HI.d.ts} +1 -1
  57. package/dist/{index-BgKghi19.d.ts → index-CH4TfcbL.d.ts} +1 -1
  58. package/dist/{index-D698fDOk.d.mts → index-TS4SGvf4.d.mts} +1 -1
  59. package/dist/{index-BZZTOfrI.d.mts → index-u08qcXq9.d.mts} +1 -1
  60. package/dist/index.d.mts +7 -7
  61. package/dist/index.d.ts +7 -7
  62. package/dist/index.js +4 -0
  63. package/dist/index.js.map +1 -1
  64. package/dist/index.mjs +4 -0
  65. package/dist/index.mjs.map +1 -1
  66. package/dist/local-evaluator/evaluator.d.mts +2 -2
  67. package/dist/local-evaluator/evaluator.d.ts +2 -2
  68. package/dist/{nextjs-93PHcE-i.d.mts → nextjs-CFA0J_4x.d.mts} +1 -1
  69. package/dist/{nextjs-t_ix2zQZ.d.ts → nextjs-DP2EpI-4.d.ts} +1 -1
  70. package/dist/{sdk-BFwzjYjl.d.mts → sdk-C8W54WZS.d.mts} +1 -1
  71. package/dist/{sdk-Chq02d82.d.ts → sdk-CwwCGDzK.d.ts} +1 -1
  72. package/dist/transport/index.d.mts +2 -2
  73. package/dist/transport/index.d.ts +2 -2
  74. package/dist/{types-CVT-sorC.d.mts → types-CbZOkIr-.d.mts} +21 -15
  75. package/dist/{types-CVT-sorC.d.ts → types-CbZOkIr-.d.ts} +21 -15
  76. package/dist/{types-CLP_TDu5.d.ts → types-DXNkr61h.d.ts} +1 -1
  77. package/dist/{types-y13mmzbA.d.mts → types-tBNFSbw_.d.mts} +1 -1
  78. package/dist/ui/index.d.mts +1 -1
  79. package/dist/ui/index.d.ts +1 -1
  80. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { Request, Response, RequestHandler } from 'express';
2
- import { a as AccessLevel, G as GatewayConfig, V as VerificationResult } from '../types-CVT-sorC.js';
2
+ import { a as AccessLevel, G as GatewayConfig, V as VerificationResult } from '../types-CbZOkIr-.js';
3
3
 
4
4
  /**
5
5
  * MCP server-side helpers — companion to `transport/mcp.ts` (which handles the
@@ -80,12 +80,27 @@ interface ParsedMcpRequest {
80
80
  */
81
81
  agentIdFromBody?: string;
82
82
  /**
83
- * Round-12 (F19): purpose from `params._meta.astrasync.purpose`.
84
- * Mirrors the `agentIdFromBody` extraction. The adapter combines this
85
- * with the `X-Astra-Purpose` header (header wins) before mapping; falls
86
- * back to `'mcp_invoke'` if both are absent.
83
+ * Round-12 (F19) + round-13 (R13-1): purpose extracted from the MCP body
84
+ * with the symmetric precedence chain. Sourced from `_meta.astrasync.purpose`
85
+ * (canonical SDK location) OR `params.arguments.purpose` (legacy /
86
+ * conventional callers). The discriminator is on `purposeSourceFromBody`.
87
+ * Adapter combines this with the `X-Astra-Purpose` header (header wins)
88
+ * before mapping; final fallback at `mcpToPdlss` is `'mcp_invoke'`.
87
89
  */
88
90
  purposeFromBody?: string;
91
+ /** Which body location resolved `purposeFromBody`. */
92
+ purposeSourceFromBody?: 'meta' | 'tool_argument';
93
+ /**
94
+ * Round-13 (R13-2): action extracted from the MCP body with the same
95
+ * symmetric chain as purpose. Sourced from `_meta.astrasync.action`
96
+ * (canonical) OR `params.arguments.action` (legacy). Adapter combines
97
+ * with the `X-Astra-Action` header (header wins) before mapping; final
98
+ * fallback at `mcpToPdlss` is the transport-layer default
99
+ * (`tools/call:<toolname>` or just `<method>`).
100
+ */
101
+ actionFromBody?: string;
102
+ /** Which body location resolved `actionFromBody`. */
103
+ actionSourceFromBody?: 'meta' | 'tool_argument';
89
104
  /** True for handshake methods that must succeed before any tool call. */
90
105
  isInitialize: boolean;
91
106
  /** True for `tools/call`. */
@@ -121,23 +136,44 @@ interface McpPdlssMapping {
121
136
  action: string;
122
137
  resource: string;
123
138
  /**
124
- * Round-12 (F19): the resolution channel header / tool-arg / default.
125
- * Adapters log this at debug level so partners can confirm header
126
- * pass-through, support can triage tickets, and we can watch
127
- * `default_mcp_invoke` decay over time.
139
+ * Round-13 (R13-1 / R13-2): the resolution channel for purpose and
140
+ * action disjoint enums sharing the same structure per
141
+ * `feedback_symmetric_fallbacks_for_symmetric_concepts.md`. Adapters
142
+ * log both at debug level so partners can confirm header / body
143
+ * pass-through, support can triage tickets, and we can watch the
144
+ * `default_*` / `transport_layer` decay over time as integrations
145
+ * mature.
146
+ *
147
+ * Round-12 (F19) used a narrower `purposeSource: 'header' |
148
+ * 'tool_argument' | 'default_mcp_invoke'` — round-13 widens to also
149
+ * carry the `_meta` source distinct from `tool_argument`, so the round-13
150
+ * R13-1 fallback (which now reads both `_meta.astrasync.purpose` AND
151
+ * `params.arguments.purpose`) can report WHICH body location resolved.
128
152
  */
129
- purposeSource: 'header' | 'tool_argument' | 'default_mcp_invoke';
153
+ purposeSource: 'header' | 'meta' | 'tool_argument' | 'default_mcp_invoke';
154
+ actionSource: 'header' | 'meta' | 'tool_argument' | 'transport_layer';
130
155
  }
131
156
  /**
132
- * Round-12 (F19): purpose resolution precedence header > tool-arg meta >
133
- * `'mcp_invoke'` default. Pre-fix the purpose was hardcoded to `mcp_invoke`
134
- * for every MCP call, which forced every MCP-integrated agent's PDLSS to
135
- * include `mcp_invoke` as an allowed purpose category. Header pass-through
136
- * lets agents declare business purpose (e.g. `shopping.purchase`) per call.
137
- * `purposeSource` lets adapters log the resolution channel for adoption
138
- * tracking + support triage.
157
+ * Round-13 (R13-1 + R13-2)canonical precedence chain documented ONCE,
158
+ * applied identically to both purpose and action. Per
159
+ * `feedback_symmetric_fallbacks_for_symmetric_concepts.md` drift
160
+ * between two paired concepts generates the same support-ticket class
161
+ * repeatedly, so the resolution order is:
162
+ *
163
+ * 1. `X-Astra-<concept>` HTTP header (caller's explicit override)
164
+ * 2. `params._meta.astrasync.<concept>` body field (canonical SDK location)
165
+ * 3. `params.arguments.<concept>` body field (legacy / conventional)
166
+ * 4. Transport-layer default:
167
+ * purpose → 'mcp_invoke'
168
+ * action → '<method>:<toolName>' (or just '<method>')
169
+ *
170
+ * The header tier is the adapter's job (it has `req.headers` access);
171
+ * this function takes the resolved `headerPurpose` / `headerAction`
172
+ * inputs and combines them with the body extraction already done in
173
+ * `parseMcpJsonRpc`. The discriminator is reported on `purposeSource` /
174
+ * `actionSource` for debug logging.
139
175
  */
140
- declare function mcpToPdlss(parsed: ParsedMcpRequest, headerPurpose?: string, toolArgumentPurpose?: string): McpPdlssMapping;
176
+ declare function mcpToPdlss(parsed: ParsedMcpRequest, headerPurpose?: string, headerAction?: string): McpPdlssMapping;
141
177
  /**
142
178
  * Recommended minimum access level per method type. The MCP middleware uses
143
179
  * this to split low-risk handshake / introspection traffic from high-risk
@@ -50,6 +50,9 @@ function hasMinimumAccess(actual, required) {
50
50
  return ACCESS_LEVEL_HIERARCHY[actual] >= ACCESS_LEVEL_HIERARCHY[required];
51
51
  }
52
52
 
53
+ // src/version.ts
54
+ var SDK_VERSION = "2.4.5";
55
+
53
56
  // src/verify.ts
54
57
  var DEFAULT_CONFIG = {
55
58
  apiBaseUrl: "https://astrasync.ai/api",
@@ -208,6 +211,7 @@ async function callVerifyAccessAPI(config, request) {
208
211
  if (requestData.runtimeChallengeOptions)
209
212
  body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;
210
213
  if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
214
+ body.sdkVersion = SDK_VERSION;
211
215
  if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
212
216
  const meta = {
213
217
  ...requestData.clientIp && { sourceIp: requestData.clientIp },
@@ -491,16 +495,17 @@ function parseMcpJsonRpc(body) {
491
495
  if (method === "initialize" && params && typeof params.protocolVersion === "string") {
492
496
  protocolVersion = params.protocolVersion;
493
497
  }
494
- let agentIdFromBody;
495
498
  const meta = params?._meta;
496
499
  const astrasyncMeta = meta?.astrasync;
500
+ const args = params?.arguments;
501
+ let agentIdFromBody;
497
502
  if (astrasyncMeta && typeof astrasyncMeta.agentId === "string") {
498
503
  agentIdFromBody = astrasyncMeta.agentId;
499
- } else {
500
- const args = params?.arguments;
501
- if (args && typeof args.agent_id === "string") agentIdFromBody = args.agent_id;
504
+ } else if (args && typeof args.agent_id === "string") {
505
+ agentIdFromBody = args.agent_id;
502
506
  }
503
- const purposeFromBody = astrasyncMeta && typeof astrasyncMeta.purpose === "string" ? astrasyncMeta.purpose : void 0;
507
+ const purposeBodyResult = extractFromMcpBody(astrasyncMeta, args, "purpose");
508
+ const actionBodyResult = extractFromMcpBody(astrasyncMeta, args, "action");
504
509
  const isInitialize = method === "initialize";
505
510
  const isToolCall = method === "tools/call";
506
511
  const isIntrospection = method === "tools/list" || method === "prompts/list" || method === "resources/list" || method === "ping" || method === "notifications/initialized";
@@ -509,22 +514,51 @@ function parseMcpJsonRpc(body) {
509
514
  ...toolName ? { toolName } : {},
510
515
  ...protocolVersion ? { protocolVersion } : {},
511
516
  ...agentIdFromBody ? { agentIdFromBody } : {},
512
- ...purposeFromBody ? { purposeFromBody } : {},
517
+ ...purposeBodyResult.value ? { purposeFromBody: purposeBodyResult.value } : {},
518
+ ...purposeBodyResult.source ? { purposeSourceFromBody: purposeBodyResult.source } : {},
519
+ ...actionBodyResult.value ? { actionFromBody: actionBodyResult.value } : {},
520
+ ...actionBodyResult.source ? { actionSourceFromBody: actionBodyResult.source } : {},
513
521
  isInitialize,
514
522
  isToolCall,
515
523
  isIntrospection
516
524
  };
517
525
  }
518
- function mcpToPdlss(parsed, headerPurpose, toolArgumentPurpose) {
519
- const action = parsed.toolName ? `${parsed.method}:${parsed.toolName}` : parsed.method;
526
+ function extractFromMcpBody(astrasyncMeta, args, key) {
527
+ if (astrasyncMeta && typeof astrasyncMeta[key] === "string") {
528
+ return { value: astrasyncMeta[key], source: "meta" };
529
+ }
530
+ if (args && typeof args[key] === "string") {
531
+ return { value: args[key], source: "tool_argument" };
532
+ }
533
+ return { value: void 0, source: void 0 };
534
+ }
535
+ function mcpToPdlss(parsed, headerPurpose, headerAction) {
520
536
  const resource = parsed.toolName ? `mcp:tool/${parsed.toolName}` : `mcp:method/${parsed.method}`;
537
+ let purpose;
538
+ let purposeSource;
521
539
  if (headerPurpose) {
522
- return { purpose: headerPurpose, action, resource, purposeSource: "header" };
540
+ purpose = headerPurpose;
541
+ purposeSource = "header";
542
+ } else if (parsed.purposeFromBody && parsed.purposeSourceFromBody) {
543
+ purpose = parsed.purposeFromBody;
544
+ purposeSource = parsed.purposeSourceFromBody;
545
+ } else {
546
+ purpose = "mcp_invoke";
547
+ purposeSource = "default_mcp_invoke";
523
548
  }
524
- if (toolArgumentPurpose) {
525
- return { purpose: toolArgumentPurpose, action, resource, purposeSource: "tool_argument" };
549
+ let action;
550
+ let actionSource;
551
+ if (headerAction) {
552
+ action = headerAction;
553
+ actionSource = "header";
554
+ } else if (parsed.actionFromBody && parsed.actionSourceFromBody) {
555
+ action = parsed.actionFromBody;
556
+ actionSource = parsed.actionSourceFromBody;
557
+ } else {
558
+ action = parsed.toolName ? `${parsed.method}:${parsed.toolName}` : parsed.method;
559
+ actionSource = "transport_layer";
526
560
  }
527
- return { purpose: "mcp_invoke", action, resource, purposeSource: "default_mcp_invoke" };
561
+ return { purpose, action, resource, purposeSource, actionSource };
528
562
  }
529
563
  function mcpRiskTier(parsed) {
530
564
  if (parsed.isInitialize || parsed.method === "notifications/initialized") return "none";
@@ -535,6 +569,11 @@ function mcpRiskTier(parsed) {
535
569
  }
536
570
 
537
571
  // src/adapters/mcp.ts
572
+ function readSingleHeader(value) {
573
+ if (typeof value === "string") return value;
574
+ if (Array.isArray(value)) return value[0];
575
+ return void 0;
576
+ }
538
577
  function defaultMcpDenied(result, req, res) {
539
578
  const id = req.body?.id ?? null;
540
579
  const status = result.verified ? 403 : 401;
@@ -629,25 +668,28 @@ function createMcpMiddleware(options) {
629
668
  toolGates,
630
669
  methodGates
631
670
  });
632
- if (minAccessLevel === "none") {
671
+ const credentials = extractCredentials(
672
+ req.headers,
673
+ req.query
674
+ );
675
+ if (effectiveAstraId) credentials.astraId = effectiveAstraId;
676
+ const shouldEnforce = minAccessLevel !== "none";
677
+ if (minAccessLevel === "none" && (!config.evaluateAlwaysIfCredentialed || !credentials.astraId)) {
633
678
  if (config.setPassThroughHeader) {
634
679
  res.setHeader("X-Astra-Gateway-Mode", "unenforced");
635
680
  res.setHeader("X-Astra-Gateway-Reason", "mcp-tier-none");
636
681
  }
637
682
  return next();
638
683
  }
639
- const credentials = extractCredentials(
640
- req.headers,
641
- req.query
642
- );
643
- if (effectiveAstraId) credentials.astraId = effectiveAstraId;
644
- const headerPurposeRaw = req.headers["x-astra-purpose"];
645
- const headerPurpose = typeof headerPurposeRaw === "string" ? headerPurposeRaw : Array.isArray(headerPurposeRaw) ? headerPurposeRaw[0] : void 0;
646
- const pdlss = mcpToPdlss(parsed, headerPurpose, parsed.purposeFromBody);
684
+ const headerPurpose = readSingleHeader(req.headers["x-astra-purpose"]);
685
+ const headerAction = readSingleHeader(req.headers["x-astra-action"]);
686
+ const pdlss = mcpToPdlss(parsed, headerPurpose, headerAction);
647
687
  if (config.debug) {
648
- console.debug("[mcp-middleware] purpose resolved", {
688
+ console.debug("[mcp-middleware] pdlss resolved", {
649
689
  purpose_source: pdlss.purposeSource,
650
- resolved_purpose: pdlss.purpose
690
+ resolved_purpose: pdlss.purpose,
691
+ action_source: pdlss.actionSource,
692
+ resolved_action: pdlss.action
651
693
  });
652
694
  }
653
695
  const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get("host")}${req.path}`;
@@ -682,6 +724,17 @@ function createMcpMiddleware(options) {
682
724
  onDenied(result, req, res);
683
725
  return;
684
726
  }
727
+ if (!shouldEnforce) {
728
+ if (config.setPassThroughHeader) {
729
+ res.setHeader("X-Astra-Gateway-Mode", "enforced");
730
+ res.setHeader("X-Astra-Gateway-Reason", "evaluated-not-enforced");
731
+ }
732
+ if (shouldRecordDecisions && sessionId) {
733
+ recordDecision(config, sessionId, "granted").catch(() => {
734
+ });
735
+ }
736
+ return next();
737
+ }
685
738
  if (!hasMinimumAccess(result.accessLevel, minAccessLevel)) {
686
739
  const insufficientFailure = {
687
740
  dimension: "access_level.insufficient",