@astrasyncai/verification-gateway 5.18.1 → 5.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +149 -0
- package/README.md +168 -4
- package/dist/adapters/express.d.mts +9 -1
- package/dist/adapters/express.d.ts +9 -1
- package/dist/adapters/express.js +96 -37
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +96 -37
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/http-pdlss.d.mts +7 -1
- package/dist/adapters/http-pdlss.d.ts +7 -1
- package/dist/adapters/http-pdlss.js +18 -2
- package/dist/adapters/http-pdlss.js.map +1 -1
- package/dist/adapters/http-pdlss.mjs +16 -1
- package/dist/adapters/http-pdlss.mjs.map +1 -1
- package/dist/adapters/kit-express.d.mts +93 -1
- package/dist/adapters/kit-express.d.ts +93 -1
- package/dist/adapters/kit-express.js +181 -2
- package/dist/adapters/kit-express.js.map +1 -1
- package/dist/adapters/kit-express.mjs +180 -2
- package/dist/adapters/kit-express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +80 -9
- package/dist/adapters/mcp.d.ts +80 -9
- package/dist/adapters/mcp.js +153 -5
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +151 -5
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +1 -1
- package/dist/adapters/nextjs.d.ts +1 -1
- package/dist/adapters/nextjs.js +41 -4
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +41 -4
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +44 -1
- package/dist/adapters/sdk.d.ts +44 -1
- package/dist/adapters/sdk.js +76 -2
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +76 -2
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.js +1 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +1 -1
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +46 -6
- package/dist/bin/astrasync-codex-hook.js +46 -6
- package/dist/bin/astrasync-guard.js +46 -6
- package/dist/bin/astrasync.js +77 -5
- package/dist/browser/background.js +46 -6
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +46 -6
- package/dist/browser/background.mjs.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.js +46 -6
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +46 -6
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/extension.js +46 -6
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +46 -6
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +1 -1
- package/dist/edge-config.d.ts +1 -1
- package/dist/edge-config.js +1 -1
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs +1 -1
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +1 -1
- package/dist/edge-core/index.d.ts +1 -1
- package/dist/edge-core/index.js +21 -2
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +21 -2
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/gateway/gateway.js +46 -6
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +46 -6
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +1 -1
- package/dist/git-trigger/git-hooks.d.ts +1 -1
- package/dist/index.d.mts +541 -29
- package/dist/index.d.ts +541 -29
- package/dist/index.js +1171 -183
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1157 -173
- package/dist/index.mjs.map +1 -1
- package/dist/registration/index.js +1 -1
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs +1 -1
- package/dist/registration/index.mjs.map +1 -1
- package/dist/transport/index.js +3 -2
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +3 -2
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-3ObBpsnf.d.mts → types-BoGjyon0.d.mts} +81 -2
- package/dist/{types-Cns4l8gX.d.mts → types-DqWPkiYl.d.mts} +90 -12
- package/dist/{types-Cns4l8gX.d.ts → types-DqWPkiYl.d.ts} +90 -12
- package/dist/{types-BoxZEYPS.d.ts → types-eWyZuz-3.d.ts} +81 -2
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/verify.d.mts +23 -2
- package/dist/verify.d.ts +23 -2
- package/dist/verify.js +23 -4
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +23 -4
- package/dist/verify.mjs.map +1 -1
- package/dist/webhooks.d.mts +16 -3
- package/dist/webhooks.d.ts +16 -3
- package/dist/webhooks.js +24 -13
- package/dist/webhooks.js.map +1 -1
- package/dist/webhooks.mjs +24 -13
- package/dist/webhooks.mjs.map +1 -1
- package/package.json +1 -1
package/dist/adapters/mcp.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Request, Response, RequestHandler } from 'express';
|
|
2
|
-
import {
|
|
2
|
+
import { e as VerificationResult, G as GatewayConfig } from '../types-DqWPkiYl.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* X-Astra-Verified-Hop — cross-hop verify-access dedupe marker.
|
|
@@ -378,23 +378,94 @@ interface McpMiddlewareOptions extends GatewayConfig {
|
|
|
378
378
|
recordDecisions?: boolean;
|
|
379
379
|
/** Forward runtime challenge (default `true`). */
|
|
380
380
|
enableRuntimeChallenge?: boolean;
|
|
381
|
+
/**
|
|
382
|
+
* 5.19.0: how a dashboard tool rule combines with a code `toolGates`
|
|
383
|
+
* entry for the same tool. With `apiKey` + `counterpartyId` set, the
|
|
384
|
+
* middleware fetches the endpoint's tool rules (`{tool, purpose, action,
|
|
385
|
+
* resource?, observe?}`, edited in the AstraSync dashboard; refreshed every
|
|
386
|
+
* `routesRefreshMs`) and turns each into a gate for that tool.
|
|
387
|
+
*
|
|
388
|
+
* - `'code'` (default): a dashboard rule can TIGHTEN a code gate but never
|
|
389
|
+
* loosen it. For a tool gated in code (object form), a dashboard
|
|
390
|
+
* `observe` rule, or one with no purpose, is ignored; a dashboard rule
|
|
391
|
+
* whose purpose or action differs is evaluated IN ADDITION — both must
|
|
392
|
+
* pass. Tools with no code gate (or a code `'observe'`) follow the
|
|
393
|
+
* dashboard rule.
|
|
394
|
+
* - `'dashboard'`: the dashboard rule wins outright for every tool it
|
|
395
|
+
* names (including `observe`).
|
|
396
|
+
*
|
|
397
|
+
* Rules flagged `legacy: true` (path rules stored on an MCP endpoint before
|
|
398
|
+
* tool rules existed) are ignored. The middleware sends its code-gated tool
|
|
399
|
+
* names on the policy fetch (`?codeGates=`) so the dashboard can mark them.
|
|
400
|
+
*/
|
|
401
|
+
toolGatePrecedence?: 'code' | 'dashboard';
|
|
402
|
+
/** 5.19.0: refresh interval (ms) for the dashboard tool rules. Default 5 minutes. */
|
|
403
|
+
routesRefreshMs?: number;
|
|
381
404
|
/**
|
|
382
405
|
* Posture when the MCP middleware itself throws an internal error.
|
|
383
|
-
* Default `'open'` for backward compatibility in SDK 2.4.13. Shadow logs
|
|
384
|
-
* record what WOULD be denied with `failOnError: 'closed'` so merchants
|
|
385
|
-
* can grep correlationId for impact analysis during the observation
|
|
386
|
-
* window. Default flips to `'closed'` in a follow-up release.
|
|
387
406
|
*
|
|
388
|
-
* 5.
|
|
389
|
-
* JSON-RPC error (HTTP 503 +
|
|
390
|
-
* identity-shaped 401
|
|
407
|
+
* 5.19.0 (behaviour change): default `'closed'` — an internal throw answers
|
|
408
|
+
* the retryable verification-unavailable JSON-RPC error (HTTP 503 +
|
|
409
|
+
* Retry-After, code -32003; never an identity-shaped 401) and the tool
|
|
410
|
+
* handler is not called. `'open'` (pass the call through unverified) is
|
|
411
|
+
* an explicit opt-in. The `[SHADOW] would-have-denied` log line is still
|
|
412
|
+
* written on every throw, with the correlationId.
|
|
391
413
|
*/
|
|
392
414
|
failOnError?: 'open' | 'closed';
|
|
393
415
|
}
|
|
416
|
+
/** 5.19.0: a dashboard tool rule as the MCP middleware evaluates it. */
|
|
417
|
+
interface DashboardToolRule {
|
|
418
|
+
tool: string;
|
|
419
|
+
purpose?: string;
|
|
420
|
+
action?: string;
|
|
421
|
+
resource?: string;
|
|
422
|
+
observe?: boolean;
|
|
423
|
+
}
|
|
424
|
+
/** The usable tool rules in a fetched policy (tool key set, not legacy), by tool name. */
|
|
425
|
+
declare function dashboardToolRules(routes: ReadonlyArray<{
|
|
426
|
+
tool?: string;
|
|
427
|
+
legacy?: boolean;
|
|
428
|
+
purpose?: string;
|
|
429
|
+
action?: string;
|
|
430
|
+
resource?: string;
|
|
431
|
+
observe?: boolean;
|
|
432
|
+
}>): Map<string, DashboardToolRule>;
|
|
433
|
+
type GateSource = 'toolGate' | 'methodGate' | 'dashboard' | 'tier' | 'discovery_default';
|
|
434
|
+
/**
|
|
435
|
+
* Resolve whether a parsed MCP request is enforced, and the gate config to
|
|
436
|
+
* apply. SDK 5.0.0 removed the access-level band — a gate's only decision is
|
|
437
|
+
* enforce vs observe.
|
|
438
|
+
*
|
|
439
|
+
* Precedence:
|
|
440
|
+
* 1. Non-tools/call without `methodGates` entry → pass through
|
|
441
|
+
* (`discovery_default`). Discovery is open — agents must list tools
|
|
442
|
+
* before invoking them.
|
|
443
|
+
* 2. `methodGates[method]` — per-method override (`'observe'` or a config)
|
|
444
|
+
* 3. tools/call: the code `toolGates[toolName]` and the dashboard tool rule
|
|
445
|
+
* (5.19.0) — see `toolGatePrecedence`. With the default `'code'`, a
|
|
446
|
+
* dashboard rule only tightens a code gate: it is ignored when it is
|
|
447
|
+
* `observe` or has no purpose, and evaluated as an `additional` gate
|
|
448
|
+
* (both must pass) when its purpose/action differ.
|
|
449
|
+
* 4. `mcpDefaultEnforced` — built-in default (tools/call → enforced)
|
|
450
|
+
*
|
|
451
|
+
* The `source` field is logged for forensics / activity-feed attribution.
|
|
452
|
+
*/
|
|
453
|
+
declare function resolveGate(parsed: ParsedMcpRequest, opts: {
|
|
454
|
+
toolGates?: Record<string, ToolGate>;
|
|
455
|
+
methodGates?: Record<string, ToolGate>;
|
|
456
|
+
dashboardRules?: Map<string, DashboardToolRule>;
|
|
457
|
+
toolGatePrecedence?: 'code' | 'dashboard';
|
|
458
|
+
}): {
|
|
459
|
+
enforce: boolean;
|
|
460
|
+
config?: ToolGateConfig;
|
|
461
|
+
/** 5.19.0: a dashboard gate evaluated in addition to `config` (both must pass). */
|
|
462
|
+
additional?: ToolGateConfig;
|
|
463
|
+
source: GateSource;
|
|
464
|
+
};
|
|
394
465
|
/**
|
|
395
466
|
* Create the MCP middleware. Attach AFTER `express.json()` — the body must
|
|
396
467
|
* already be a parsed object.
|
|
397
468
|
*/
|
|
398
469
|
declare function createMcpMiddleware(options: McpMiddlewareOptions): RequestHandler;
|
|
399
470
|
|
|
400
|
-
export { MCP_VERIFIED_HOP_HEADER, type McpMiddlewareOptions, type ParsedMcpRequest, type ToolGate, type ToolGateConfig, createMcpMiddleware, isVerifiedHopValidFor, mcpDefaultEnforced, mcpToPdlss, parseMcpJsonRpc, parseVerifiedHop, serializeVerifiedHop };
|
|
471
|
+
export { type DashboardToolRule, MCP_VERIFIED_HOP_HEADER, type McpMiddlewareOptions, type ParsedMcpRequest, type ToolGate, type ToolGateConfig, createMcpMiddleware, dashboardToolRules, isVerifiedHopValidFor, mcpDefaultEnforced, mcpToPdlss, parseMcpJsonRpc, parseVerifiedHop, resolveGate, serializeVerifiedHop };
|
package/dist/adapters/mcp.js
CHANGED
|
@@ -22,17 +22,19 @@ var mcp_exports = {};
|
|
|
22
22
|
__export(mcp_exports, {
|
|
23
23
|
MCP_VERIFIED_HOP_HEADER: () => MCP_VERIFIED_HOP_HEADER,
|
|
24
24
|
createMcpMiddleware: () => createMcpMiddleware,
|
|
25
|
+
dashboardToolRules: () => dashboardToolRules,
|
|
25
26
|
isVerifiedHopValidFor: () => isVerifiedHopValidFor,
|
|
26
27
|
mcpDefaultEnforced: () => mcpDefaultEnforced,
|
|
27
28
|
mcpToPdlss: () => mcpToPdlss,
|
|
28
29
|
parseMcpJsonRpc: () => parseMcpJsonRpc,
|
|
29
30
|
parseVerifiedHop: () => parseVerifiedHop,
|
|
31
|
+
resolveGate: () => resolveGate,
|
|
30
32
|
serializeVerifiedHop: () => serializeVerifiedHop
|
|
31
33
|
});
|
|
32
34
|
module.exports = __toCommonJS(mcp_exports);
|
|
33
35
|
|
|
34
36
|
// src/version.ts
|
|
35
|
-
var SDK_VERSION = "5.
|
|
37
|
+
var SDK_VERSION = "5.19.0";
|
|
36
38
|
|
|
37
39
|
// src/http.ts
|
|
38
40
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -274,7 +276,12 @@ function getCacheKey(request, counterpartyId) {
|
|
|
274
276
|
// its start_checkout quote was served the cached quote-leg grant (no
|
|
275
277
|
// settlementOutcome) and never reached the backend. Same F-A1-07 bug class.
|
|
276
278
|
request.commercePhase || "",
|
|
277
|
-
request.checkoutSessionId || ""
|
|
279
|
+
request.checkoutSessionId || "",
|
|
280
|
+
// 5.19.0: a route_config purpose claim is checked server-side against
|
|
281
|
+
// the stored rule for this method + path, so it changes the verdict.
|
|
282
|
+
request.purposeSource || "",
|
|
283
|
+
request.requestMethod || "",
|
|
284
|
+
request.requestPath || ""
|
|
278
285
|
].join("|");
|
|
279
286
|
}
|
|
280
287
|
function getCachedResult(request, counterpartyId) {
|
|
@@ -482,6 +489,11 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
482
489
|
if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
|
|
483
490
|
if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
|
|
484
491
|
if (requestData.attemptId) body.attemptId = requestData.attemptId;
|
|
492
|
+
if (requestData.purposeSource) body.purposeSource = requestData.purposeSource;
|
|
493
|
+
if (requestData.agentArtifacts) body.agentArtifacts = requestData.agentArtifacts;
|
|
494
|
+
if (requestData.metadata) body.metadata = requestData.metadata;
|
|
495
|
+
if (requestData.requestMethod) body.requestMethod = requestData.requestMethod;
|
|
496
|
+
if (requestData.requestPath) body.requestPath = requestData.requestPath;
|
|
485
497
|
if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;
|
|
486
498
|
body.sdkVersion = SDK_VERSION;
|
|
487
499
|
if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
|
|
@@ -671,9 +683,18 @@ async function verify(config, request, options) {
|
|
|
671
683
|
if (!apiResponse.access?.allowed) {
|
|
672
684
|
const aggregatedFailures = apiResponse.access?.failures;
|
|
673
685
|
const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true && !aggregatedFailures?.some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));
|
|
686
|
+
const denyAgent = idVerifiedFromBackend && apiResponse.agent ? {
|
|
687
|
+
astraId: apiResponse.agent.astraId,
|
|
688
|
+
name: apiResponse.agent.name,
|
|
689
|
+
trustScore: apiResponse.agent.trustScore,
|
|
690
|
+
trustLevel: getTrustLevel(apiResponse.agent.trustScore),
|
|
691
|
+
blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
|
|
692
|
+
status: apiResponse.agent.agentStatus
|
|
693
|
+
} : void 0;
|
|
674
694
|
const result2 = {
|
|
675
695
|
identityVerified: idVerifiedFromBackend,
|
|
676
696
|
policyAllowed: false,
|
|
697
|
+
...denyAgent && { agent: denyAgent },
|
|
677
698
|
// 3.6.0: the server now emits top-level denialReasons on every deny
|
|
678
699
|
// path — prefer it verbatim; the failure-message reconstruction remains
|
|
679
700
|
// as the fallback for older backends.
|
|
@@ -822,6 +843,34 @@ async function recordDecision(config, sessionId, decision, reason) {
|
|
|
822
843
|
}).catch(() => {
|
|
823
844
|
});
|
|
824
845
|
}
|
|
846
|
+
var refusedPolicyReads = /* @__PURE__ */ new Set();
|
|
847
|
+
async function fetchRoutes(config, counterpartyId, options = {}) {
|
|
848
|
+
if (!counterpartyId) return null;
|
|
849
|
+
const headers = { "Content-Type": "application/json" };
|
|
850
|
+
if (config.apiKey) {
|
|
851
|
+
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
852
|
+
headers["X-API-Key"] = config.apiKey;
|
|
853
|
+
}
|
|
854
|
+
try {
|
|
855
|
+
const response = await sdkFetch(
|
|
856
|
+
`${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes` + (options.codeGates?.length ? `?codeGates=${options.codeGates.map(encodeURIComponent).join(",")}` : ""),
|
|
857
|
+
{ method: "GET", headers }
|
|
858
|
+
);
|
|
859
|
+
if (!response.ok) {
|
|
860
|
+
if ((response.status === 401 || response.status === 403 || response.status === 404) && !refusedPolicyReads.has(counterpartyId)) {
|
|
861
|
+
refusedPolicyReads.add(counterpartyId);
|
|
862
|
+
console.error(
|
|
863
|
+
`[VerificationGateway] the configured API key cannot read the route policy for ${counterpartyId} (HTTP ${response.status}). The key belongs to an account that does not own this endpoint, or the counterpartyId is wrong. The gateway is in PASS-THROUGH mode: no agent is being verified.`
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
return null;
|
|
867
|
+
}
|
|
868
|
+
const body = await response.json();
|
|
869
|
+
return body.data?.routes ?? [];
|
|
870
|
+
} catch {
|
|
871
|
+
return null;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
825
874
|
|
|
826
875
|
// src/metadata-capture.ts
|
|
827
876
|
var CAPTURE_SCHEMA_VERSION = 1;
|
|
@@ -1144,6 +1193,42 @@ async function awaitStepUpApproval(config, approval) {
|
|
|
1144
1193
|
return "timeout";
|
|
1145
1194
|
}
|
|
1146
1195
|
|
|
1196
|
+
// src/adapters/routes-cache.ts
|
|
1197
|
+
var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
|
|
1198
|
+
function createRoutesCache(config, opts = {}) {
|
|
1199
|
+
const refreshMs = opts.refreshMs ?? DEFAULT_ROUTES_REFRESH_MS;
|
|
1200
|
+
let cached = [];
|
|
1201
|
+
let lastFetchAt = 0;
|
|
1202
|
+
let refreshing = null;
|
|
1203
|
+
async function refresh() {
|
|
1204
|
+
if (!config.counterpartyId) {
|
|
1205
|
+
opts.onNoCounterparty?.();
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
const fetched = await fetchRoutes(config, config.counterpartyId, opts.fetchOptions);
|
|
1209
|
+
if (fetched) {
|
|
1210
|
+
cached = fetched;
|
|
1211
|
+
lastFetchAt = Date.now();
|
|
1212
|
+
opts.onFetched?.(fetched);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
function start() {
|
|
1216
|
+
refreshing = refresh().catch(() => void 0).finally(() => {
|
|
1217
|
+
refreshing = null;
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
start();
|
|
1221
|
+
return {
|
|
1222
|
+
async current() {
|
|
1223
|
+
if (refreshing) await refreshing.catch(() => {
|
|
1224
|
+
});
|
|
1225
|
+
if (config.counterpartyId && Date.now() - lastFetchAt > refreshMs) start();
|
|
1226
|
+
return cached;
|
|
1227
|
+
},
|
|
1228
|
+
peek: () => cached
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1147
1232
|
// src/verified-hop.ts
|
|
1148
1233
|
var MCP_VERIFIED_HOP_HEADER = "X-Astra-Verified-Hop";
|
|
1149
1234
|
var MCP_VERIFIED_HOP_MAX_AGE_MS = 6e4;
|
|
@@ -1364,6 +1449,21 @@ function defaultMcpDenied(result, req, res) {
|
|
|
1364
1449
|
}
|
|
1365
1450
|
});
|
|
1366
1451
|
}
|
|
1452
|
+
function dashboardToolRules(routes) {
|
|
1453
|
+
const out = /* @__PURE__ */ new Map();
|
|
1454
|
+
for (const r of routes) {
|
|
1455
|
+
if (typeof r.tool !== "string" || !r.tool || r.legacy === true) continue;
|
|
1456
|
+
if (out.has(r.tool)) continue;
|
|
1457
|
+
out.set(r.tool, {
|
|
1458
|
+
tool: r.tool,
|
|
1459
|
+
...r.purpose && { purpose: r.purpose },
|
|
1460
|
+
...r.action && { action: r.action },
|
|
1461
|
+
...r.resource && { resource: r.resource },
|
|
1462
|
+
...r.observe === true && { observe: true }
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
return out;
|
|
1466
|
+
}
|
|
1367
1467
|
function resolveGate(parsed, opts) {
|
|
1368
1468
|
if (!parsed.toolName) {
|
|
1369
1469
|
const methodGate = opts.methodGates?.[parsed.method];
|
|
@@ -1373,8 +1473,30 @@ function resolveGate(parsed, opts) {
|
|
|
1373
1473
|
return { enforce: false, source: "discovery_default" };
|
|
1374
1474
|
}
|
|
1375
1475
|
const toolGate = opts.toolGates?.[parsed.toolName];
|
|
1476
|
+
const rule = opts.dashboardRules?.get(parsed.toolName);
|
|
1477
|
+
const fromRule = (r) => r.observe ? { enforce: false, source: "dashboard" } : {
|
|
1478
|
+
enforce: true,
|
|
1479
|
+
config: {
|
|
1480
|
+
...r.purpose && { purpose: r.purpose },
|
|
1481
|
+
...r.action && { action: r.action },
|
|
1482
|
+
...r.resource && { resource: r.resource }
|
|
1483
|
+
},
|
|
1484
|
+
source: "dashboard"
|
|
1485
|
+
};
|
|
1486
|
+
const codeGated = toolGate !== void 0 && toolGate !== "observe";
|
|
1487
|
+
if (rule && (opts.toolGatePrecedence === "dashboard" || !codeGated)) return fromRule(rule);
|
|
1376
1488
|
if (toolGate !== void 0) {
|
|
1377
|
-
|
|
1489
|
+
if (toolGate === "observe") return { enforce: false, source: "toolGate" };
|
|
1490
|
+
if (rule && !rule.observe && rule.purpose && (rule.purpose !== toolGate.purpose || rule.action && rule.action !== toolGate.action)) {
|
|
1491
|
+
const extra = fromRule(rule).config;
|
|
1492
|
+
return {
|
|
1493
|
+
enforce: true,
|
|
1494
|
+
config: toolGate,
|
|
1495
|
+
additional: { ...extra, ...!extra.resource && toolGate.resource && { resource: toolGate.resource } },
|
|
1496
|
+
source: "toolGate"
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
return { enforce: true, config: toolGate, source: "toolGate" };
|
|
1378
1500
|
}
|
|
1379
1501
|
return { enforce: mcpDefaultEnforced(parsed), source: "tier" };
|
|
1380
1502
|
}
|
|
@@ -1389,13 +1511,20 @@ function createMcpMiddleware(options) {
|
|
|
1389
1511
|
verifiedHopMaxAgeMs,
|
|
1390
1512
|
recordDecisions,
|
|
1391
1513
|
enableRuntimeChallenge = true,
|
|
1392
|
-
failOnError = "
|
|
1514
|
+
failOnError = "closed",
|
|
1515
|
+
toolGatePrecedence = "code",
|
|
1516
|
+
routesRefreshMs,
|
|
1393
1517
|
...config
|
|
1394
1518
|
} = options;
|
|
1395
1519
|
if (config.apiBaseUrl) {
|
|
1396
1520
|
prefetchWellKnown(config.apiBaseUrl).catch(() => {
|
|
1397
1521
|
});
|
|
1398
1522
|
}
|
|
1523
|
+
const codeGatedTools = Object.entries(toolGates ?? {}).filter(([, g]) => g !== "observe").map(([name]) => name);
|
|
1524
|
+
const rulesCache = config.apiKey && config.counterpartyId ? createRoutesCache(config, {
|
|
1525
|
+
...routesRefreshMs !== void 0 && { refreshMs: routesRefreshMs },
|
|
1526
|
+
...codeGatedTools.length && { fetchOptions: { codeGates: codeGatedTools } }
|
|
1527
|
+
}) : void 0;
|
|
1399
1528
|
return async (req, res, next) => {
|
|
1400
1529
|
try {
|
|
1401
1530
|
if (skip) return next();
|
|
@@ -1444,9 +1573,12 @@ function createMcpMiddleware(options) {
|
|
|
1444
1573
|
return next();
|
|
1445
1574
|
}
|
|
1446
1575
|
}
|
|
1576
|
+
const dashboardRules = rulesCache ? dashboardToolRules(await rulesCache.current()) : void 0;
|
|
1447
1577
|
const gateDecision = resolveGate(parsed, {
|
|
1448
1578
|
toolGates,
|
|
1449
|
-
methodGates
|
|
1579
|
+
methodGates,
|
|
1580
|
+
dashboardRules,
|
|
1581
|
+
toolGatePrecedence
|
|
1450
1582
|
});
|
|
1451
1583
|
const credentials = extractCredentials(
|
|
1452
1584
|
req.headers,
|
|
@@ -1544,6 +1676,20 @@ function createMcpMiddleware(options) {
|
|
|
1544
1676
|
result.stepUpOutcome = outcome;
|
|
1545
1677
|
}
|
|
1546
1678
|
}
|
|
1679
|
+
if (shouldEnforce && gateDecision.additional?.purpose && result.policyAllowed && !requiresHumanApproval(result)) {
|
|
1680
|
+
const extra = mcpToPdlss(parsed, req.path, void 0, void 0, gateDecision.additional);
|
|
1681
|
+
const extraResult = await verify(config, {
|
|
1682
|
+
...verificationRequest,
|
|
1683
|
+
purpose: extra.purpose,
|
|
1684
|
+
action: extra.action,
|
|
1685
|
+
resource: extra.resource
|
|
1686
|
+
});
|
|
1687
|
+
if (!extraResult.policyAllowed || requiresHumanApproval(extraResult)) {
|
|
1688
|
+
result = extraResult;
|
|
1689
|
+
req.agentVerification = result;
|
|
1690
|
+
sessionId = result.sessionId;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1547
1693
|
if (!result.policyAllowed && isVerificationUnavailable(result) && (!shouldEnforce || isReadOnlyMcpMethod(parsed) && !credentials.astraId && !credentials.jwt)) {
|
|
1548
1694
|
res.setHeader(VERIFICATION_STATUS_HEADER, "unavailable");
|
|
1549
1695
|
return next();
|
|
@@ -1629,11 +1775,13 @@ function createMcpMiddleware(options) {
|
|
|
1629
1775
|
0 && (module.exports = {
|
|
1630
1776
|
MCP_VERIFIED_HOP_HEADER,
|
|
1631
1777
|
createMcpMiddleware,
|
|
1778
|
+
dashboardToolRules,
|
|
1632
1779
|
isVerifiedHopValidFor,
|
|
1633
1780
|
mcpDefaultEnforced,
|
|
1634
1781
|
mcpToPdlss,
|
|
1635
1782
|
parseMcpJsonRpc,
|
|
1636
1783
|
parseVerifiedHop,
|
|
1784
|
+
resolveGate,
|
|
1637
1785
|
serializeVerifiedHop
|
|
1638
1786
|
});
|
|
1639
1787
|
//# sourceMappingURL=mcp.js.map
|