@frontmcp/plugin-approval 1.8.7 → 1.9.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/approval/types.d.ts +2 -1
- package/approval/types.d.ts.map +1 -1
- package/esm/index.mjs +95 -19
- package/esm/package.json +4 -4
- package/hooks/approval-check.hook.d.ts +19 -0
- package/hooks/approval-check.hook.d.ts.map +1 -1
- package/index.js +87 -15
- package/package.json +4 -4
- package/stores/approval-storage.store.d.ts +8 -0
- package/stores/approval-storage.store.d.ts.map +1 -1
- package/stores/approval-store.interface.d.ts +9 -0
- package/stores/approval-store.interface.d.ts.map +1 -1
package/approval/types.d.ts
CHANGED
|
@@ -221,7 +221,8 @@ export interface ToolApprovalRequirement {
|
|
|
221
221
|
maxTtlMs?: number;
|
|
222
222
|
/**
|
|
223
223
|
* Whether to prompt on each call even if approved.
|
|
224
|
-
* For highly sensitive operations
|
|
224
|
+
* For highly sensitive operations: each approval lets exactly one call through and is used up by
|
|
225
|
+
* it, so the next call is refused with `pending` until the caller approves again.
|
|
225
226
|
* @default false
|
|
226
227
|
*/
|
|
227
228
|
alwaysPrompt?: boolean;
|
package/approval/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/approval/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/approval/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;GAEG;AACH,oBAAY,aAAa;IACvB,8DAA8D;IAC9D,OAAO,YAAY;IAEnB,oEAAoE;IACpE,IAAI,SAAS;IAEb,sEAAsE;IACtE,YAAY,iBAAiB;IAE7B,0DAA0D;IAC1D,aAAa,kBAAkB;IAE/B,wEAAwE;IACxE,gBAAgB,qBAAqB;CACtC;AAMD;;GAEG;AACH,oBAAY,aAAa;IACvB,oCAAoC;IACpC,OAAO,YAAY;IAEnB,2CAA2C;IAC3C,QAAQ,aAAa;IAErB,yCAAyC;IACzC,MAAM,WAAW;IAEjB,4CAA4C;IAC5C,OAAO,YAAY;CACpB;AAMD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IAEb,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IAEnB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAMD;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,kBAAkB,GAC1B,MAAM,GACN,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,OAAO,GACP,KAAK,GACL,OAAO,GACP,MAAM,GACN,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,UAAU,GAAG,YAAY,GAAG,OAAO,GAAG,KAAK,CAAC;AAMzF;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IAEpB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;IAEnB,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,eAAe;IAC9B,0CAA0C;IAC1C,MAAM,EAAE,kBAAkB,CAAC;IAE3B,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,oCAAoC;IACpC,MAAM,CAAC,EAAE,cAAc,CAAC;IAExB,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;AAMD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,kBAAkB,GAAG,QAAQ,GAAG,aAAa,CAAC;AAEjF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,0CAA0C;IAC1C,MAAM,EAAE,oBAAoB,CAAC;IAE7B,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,uCAAuC;IACvC,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAMD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IAEf,6BAA6B;IAC7B,KAAK,EAAE,aAAa,CAAC;IAErB,6BAA6B;IAC7B,KAAK,EAAE,aAAa,CAAC;IAErB,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAElB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,eAAe,CAAC;IAE1B,uDAAuD;IACvD,SAAS,EAAE,eAAe,CAAC;IAE3B,8CAA8C;IAC9C,aAAa,CAAC,EAAE,eAAe,EAAE,CAAC;IAElC,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEnC,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,oCAAoC;IACpC,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B,4BAA4B;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAMD;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAEjF;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAE/D;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,YAAY,CAAC,EAAE,aAAa,CAAC;IAE7B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,EAAE,CAAC;IAEhC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAE5B;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,eAAe,EAAE,CAAC;CACzC"}
|
package/esm/index.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
var __frontmcpModule = typeof process !== "undefined" && typeof process.getBuiltinModule === "function" ? process.getBuiltinModule("module") : undefined;
|
|
2
|
+
const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.url || 'file:///') : undefined;
|
|
3
|
+
// __frontmcpCreateRequire
|
|
1
4
|
var __defProp = Object.defineProperty;
|
|
2
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
6
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -23,7 +26,7 @@ import {
|
|
|
23
26
|
FRONTMCP_CONTEXT,
|
|
24
27
|
Plugin as Plugin2,
|
|
25
28
|
ProviderScope as ProviderScope4,
|
|
26
|
-
ScopeEntry
|
|
29
|
+
ScopeEntry as ScopeEntry2
|
|
27
30
|
} from "@frontmcp/sdk";
|
|
28
31
|
|
|
29
32
|
// plugins/plugin-approval/src/approval/errors.ts
|
|
@@ -255,7 +258,7 @@ var ChallengeServiceToken = /* @__PURE__ */ Symbol.for(
|
|
|
255
258
|
);
|
|
256
259
|
|
|
257
260
|
// plugins/plugin-approval/src/hooks/approval-check.hook.ts
|
|
258
|
-
import { DynamicPlugin, Plugin, ToolHook } from "@frontmcp/sdk";
|
|
261
|
+
import { DynamicPlugin, isEntryGatedBy, Plugin, ScopeEntry, ToolHook } from "@frontmcp/sdk";
|
|
259
262
|
|
|
260
263
|
// plugins/plugin-approval/src/approval/schemas.ts
|
|
261
264
|
import { z } from "@frontmcp/lazy-zod";
|
|
@@ -474,7 +477,7 @@ function isApiGrantor(grantor) {
|
|
|
474
477
|
}
|
|
475
478
|
|
|
476
479
|
// plugins/plugin-approval/src/hooks/approval-check.hook.ts
|
|
477
|
-
var
|
|
480
|
+
var decidedCalls = /* @__PURE__ */ new WeakSet();
|
|
478
481
|
var ApprovalCheckPlugin = class extends DynamicPlugin {
|
|
479
482
|
async checkApproval(flowCtx) {
|
|
480
483
|
const { tool, toolContext } = flowCtx.state;
|
|
@@ -489,17 +492,19 @@ var ApprovalCheckPlugin = class extends DynamicPlugin {
|
|
|
489
492
|
if (approvalConfig.skipApproval) {
|
|
490
493
|
return;
|
|
491
494
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
passedApprovalStores.set(toolContext, passedStores);
|
|
498
|
-
}
|
|
499
|
-
async enforceApproval(flowCtx, tool, toolContext, approvalConfig, approvalStore) {
|
|
495
|
+
if (decidedCalls.has(toolContext)) return;
|
|
496
|
+
await this.enforceApproval(flowCtx, tool, toolContext, approvalConfig, this.coveringStores(tool));
|
|
497
|
+
decidedCalls.add(toolContext);
|
|
498
|
+
}
|
|
499
|
+
async enforceApproval(flowCtx, tool, toolContext, approvalConfig, stores) {
|
|
500
500
|
const { sessionId, userId } = resolveApprovalIdentity(toolContext.tryGetContext?.());
|
|
501
501
|
const currentContext = this.getCurrentContext(flowCtx);
|
|
502
|
-
const
|
|
502
|
+
const stored = [];
|
|
503
|
+
for (const store of stores) {
|
|
504
|
+
const records2 = await this.readApprovals(store, tool.fullName, sessionId, userId, currentContext);
|
|
505
|
+
stored.push(...records2.map((record) => ({ store, record })));
|
|
506
|
+
}
|
|
507
|
+
const records = stored.map(({ record }) => record);
|
|
503
508
|
if (records.some((record) => record.state === "denied" /* DENIED */)) {
|
|
504
509
|
throw new ApprovalRequiredError({
|
|
505
510
|
toolId: tool.fullName,
|
|
@@ -511,12 +516,13 @@ var ApprovalCheckPlugin = class extends DynamicPlugin {
|
|
|
511
516
|
return;
|
|
512
517
|
}
|
|
513
518
|
const approved = records.filter((record) => record.state === "approved" /* APPROVED */);
|
|
514
|
-
if (approvalConfig.alwaysPrompt) {
|
|
515
|
-
await this.handleApprovalRequired(flowCtx, approvalConfig, approved[0]);
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
519
|
const now = Date.now();
|
|
519
|
-
|
|
520
|
+
const usable = stored.filter(({ record }) => isApprovalUsable(record, approvalConfig, now));
|
|
521
|
+
if (approvalConfig.alwaysPrompt) {
|
|
522
|
+
for (const { store, record } of usable) {
|
|
523
|
+
if (await this.consumeApproval(store, record, sessionId, userId, currentContext)) return;
|
|
524
|
+
}
|
|
525
|
+
} else if (usable.length > 0) {
|
|
520
526
|
return;
|
|
521
527
|
}
|
|
522
528
|
await this.handleApprovalRequired(
|
|
@@ -525,6 +531,47 @@ var ApprovalCheckPlugin = class extends DynamicPlugin {
|
|
|
525
531
|
approved.find((record) => isApprovalExpired(record, approvalConfig, now)) ?? approved[0]
|
|
526
532
|
);
|
|
527
533
|
}
|
|
534
|
+
/**
|
|
535
|
+
* The stores of every approval gate that runs for the tool: this plugin's, and that of each other
|
|
536
|
+
* `ApprovalCheckPlugin` whose hook the scope runs for it (an `ApprovalPlugin` on the server and
|
|
537
|
+
* one on the tool's app, say). One store shared by two gates counts once.
|
|
538
|
+
*/
|
|
539
|
+
coveringStores(tool) {
|
|
540
|
+
const stores = /* @__PURE__ */ new Set([this.get(ApprovalStoreToken)]);
|
|
541
|
+
let scope;
|
|
542
|
+
try {
|
|
543
|
+
scope = this.get(ScopeEntry);
|
|
544
|
+
} catch {
|
|
545
|
+
scope = void 0;
|
|
546
|
+
}
|
|
547
|
+
if (!scope?.hooks) return [...stores];
|
|
548
|
+
for (const hook of scope.hooks.getFlowHooks("tools:call-tool")) {
|
|
549
|
+
const gate = hook.metadata.target;
|
|
550
|
+
if (gate instanceof ApprovalCheckPlugin && isEntryGatedBy(scope, { tool }, gate)) {
|
|
551
|
+
stores.add(gate.get(ApprovalStoreToken));
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return [...stores];
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Use up an approval of an `alwaysPrompt` tool, reporting whether this call got it.
|
|
558
|
+
*
|
|
559
|
+
* `consumeApproval()` deletes exactly that record, and only one of two calls racing for it wins.
|
|
560
|
+
* A store without it has the caller's approvals of the tool revoked instead, which leaves none
|
|
561
|
+
* standing but cannot tell two concurrent calls apart.
|
|
562
|
+
*/
|
|
563
|
+
async consumeApproval(store, record, sessionId, userId, context) {
|
|
564
|
+
if (store.consumeApproval) {
|
|
565
|
+
return store.consumeApproval(record, sessionId, userId, context);
|
|
566
|
+
}
|
|
567
|
+
return store.revokeApproval({
|
|
568
|
+
toolId: record.toolId,
|
|
569
|
+
sessionId,
|
|
570
|
+
userId,
|
|
571
|
+
revokedBy: "system",
|
|
572
|
+
reason: "Used by a call of an alwaysPrompt tool"
|
|
573
|
+
});
|
|
574
|
+
}
|
|
528
575
|
/**
|
|
529
576
|
* The caller's approvals and denials of the tool, from the context it runs in too. Stores
|
|
530
577
|
* without `getApprovals()` give the one record `getApproval()` picks.
|
|
@@ -959,11 +1006,15 @@ import { Provider as Provider3, ProviderScope as ProviderScope3 } from "@frontmc
|
|
|
959
1006
|
import {
|
|
960
1007
|
createMemoryStorage as createMemoryStorage2,
|
|
961
1008
|
createNamespacedStorage,
|
|
962
|
-
createStorage as createStorage2
|
|
1009
|
+
createStorage as createStorage2,
|
|
1010
|
+
StorageNotSupportedError
|
|
963
1011
|
} from "@frontmcp/utils";
|
|
964
1012
|
function escapePattern(str) {
|
|
965
1013
|
return str.replace(/[*?[\]\\]/g, "\\$&");
|
|
966
1014
|
}
|
|
1015
|
+
function isSameRecord(a, b) {
|
|
1016
|
+
return a.toolId === b.toolId && a.state === b.state && a.scope === b.scope && a.grantedAt === b.grantedAt && a.sessionId === b.sessionId && a.userId === b.userId && a.context?.type === b.context?.type && a.context?.identifier === b.context?.identifier;
|
|
1017
|
+
}
|
|
967
1018
|
var REVOCATION_RETENTION_SECONDS = 24 * 60 * 60;
|
|
968
1019
|
var TIME_LIMITED_KEY_SEGMENT = `scope:${"time_limited" /* TIME_LIMITED */}`;
|
|
969
1020
|
var ApprovalStorageStore = class {
|
|
@@ -1201,6 +1252,31 @@ var ApprovalStorageStore = class {
|
|
|
1201
1252
|
await this.storage.mdelete(keysToDelete);
|
|
1202
1253
|
return true;
|
|
1203
1254
|
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Deletes the key that holds `record`, if it still does, and reports whether this call deleted
|
|
1257
|
+
* it. Two calls racing for one approval both find it, but only one deletes it, and a denial or
|
|
1258
|
+
* approval written to the key after it was read is kept.
|
|
1259
|
+
*/
|
|
1260
|
+
async consumeApproval(record, sessionId, userId, context) {
|
|
1261
|
+
this.ensureInitialized();
|
|
1262
|
+
for (const key of this.callerKeys(record.toolId, sessionId, userId, context)) {
|
|
1263
|
+
const storedValue = await this.storage.get(key);
|
|
1264
|
+
const stored = this.parseRecord(storedValue);
|
|
1265
|
+
if (storedValue !== null && stored && isSameRecord(stored, record)) {
|
|
1266
|
+
return this.deleteIfUnchanged(key, storedValue);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
return false;
|
|
1270
|
+
}
|
|
1271
|
+
/** Deletes `key` only while it still holds `storedValue`, in one step where the backend allows. */
|
|
1272
|
+
async deleteIfUnchanged(key, storedValue) {
|
|
1273
|
+
try {
|
|
1274
|
+
return await this.storage.deleteIfEquals(key, storedValue);
|
|
1275
|
+
} catch (error) {
|
|
1276
|
+
if (!(error instanceof StorageNotSupportedError)) throw error;
|
|
1277
|
+
return this.storage.delete(key);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1204
1280
|
async getRevocations(toolId, sessionId, userId, context) {
|
|
1205
1281
|
this.ensureInitialized();
|
|
1206
1282
|
const records = [];
|
|
@@ -1367,7 +1443,7 @@ __publicField(ApprovalPlugin, "dynamicProviders", (options) => {
|
|
|
1367
1443
|
name: "approval:service",
|
|
1368
1444
|
provide: ApprovalServiceToken,
|
|
1369
1445
|
scope: ProviderScope4.CONTEXT,
|
|
1370
|
-
inject: () => [ApprovalStoreToken, FRONTMCP_CONTEXT,
|
|
1446
|
+
inject: () => [ApprovalStoreToken, FRONTMCP_CONTEXT, ScopeEntry2],
|
|
1371
1447
|
useFactory: (store, ctx, scope) => {
|
|
1372
1448
|
const { sessionId, userId } = resolveApprovalIdentity(ctx);
|
|
1373
1449
|
const requirementOf = (toolId) => {
|
package/esm/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugin-approval",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Approval plugin for FrontMCP - tool authorization workflow with PKCE webhook security",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -49,13 +49,13 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@frontmcp/sdk": "1.
|
|
53
|
-
"@frontmcp/utils": "1.
|
|
52
|
+
"@frontmcp/sdk": "1.9.0",
|
|
53
|
+
"@frontmcp/utils": "1.9.0",
|
|
54
54
|
"ioredis": "^5.8.0",
|
|
55
55
|
"reflect-metadata": "^0.2.2"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@frontmcp/lazy-zod": "1.
|
|
58
|
+
"@frontmcp/lazy-zod": "1.9.0",
|
|
59
59
|
"@vercel/kv": "^2.0.0 || ^3.0.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependenciesMeta": {
|
|
@@ -17,9 +17,28 @@ export default class ApprovalCheckPlugin extends DynamicPlugin<Record<string, ne
|
|
|
17
17
|
* Runs for the tools of the app the plugin is installed on and, `appliesTo: 'uncovered-apps'`,
|
|
18
18
|
* for the tools of any app with no approval gate of its own, so an `approval` tool is never left
|
|
19
19
|
* ungated because the plugin sits on a different app.
|
|
20
|
+
*
|
|
21
|
+
* A tool more than one gate covers (an `ApprovalPlugin` on the server and one on the tool's app)
|
|
22
|
+
* is decided once, over all their stores: an approval in any of them lets it run, a denial in any
|
|
23
|
+
* of them refuses it. It used to need an approval in each store (#678), so a grant through
|
|
24
|
+
* `this.approval`, which writes to one of them, never let it run.
|
|
20
25
|
*/
|
|
21
26
|
checkApproval(flowCtx: FlowCtxOf<'tools:call-tool'>): Promise<void>;
|
|
22
27
|
private enforceApproval;
|
|
28
|
+
/**
|
|
29
|
+
* The stores of every approval gate that runs for the tool: this plugin's, and that of each other
|
|
30
|
+
* `ApprovalCheckPlugin` whose hook the scope runs for it (an `ApprovalPlugin` on the server and
|
|
31
|
+
* one on the tool's app, say). One store shared by two gates counts once.
|
|
32
|
+
*/
|
|
33
|
+
private coveringStores;
|
|
34
|
+
/**
|
|
35
|
+
* Use up an approval of an `alwaysPrompt` tool, reporting whether this call got it.
|
|
36
|
+
*
|
|
37
|
+
* `consumeApproval()` deletes exactly that record, and only one of two calls racing for it wins.
|
|
38
|
+
* A store without it has the caller's approvals of the tool revoked instead, which leaves none
|
|
39
|
+
* standing but cannot tell two concurrent calls apart.
|
|
40
|
+
*/
|
|
41
|
+
private consumeApproval;
|
|
23
42
|
/**
|
|
24
43
|
* The caller's approvals and denials of the tool, from the context it runs in too. Stores
|
|
25
44
|
* without `getApprovals()` give the one record `getApproval()` picks.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-check.hook.d.ts","sourceRoot":"","sources":["../../src/hooks/approval-check.hook.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"approval-check.hook.d.ts","sourceRoot":"","sources":["../../src/hooks/approval-check.hook.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAgD,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAwB5G;;;;;GAKG;AAOH,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACnF;;;;;;;;;;;OAWG;IAEG,aAAa,CAAC,OAAO,EAAE,SAAS,CAAC,iBAAiB,CAAC;YAwB3C,eAAe;IAqD7B;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAoBtB;;;;;;OAMG;YACW,eAAe;IAmB7B;;;OAGG;YACW,aAAa;IAc3B;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,oBAAoB;YAUd,sBAAsB;CAsBrC"}
|
package/index.js
CHANGED
|
@@ -526,7 +526,7 @@ function isApiGrantor(grantor) {
|
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
// plugins/plugin-approval/src/hooks/approval-check.hook.ts
|
|
529
|
-
var
|
|
529
|
+
var decidedCalls = /* @__PURE__ */ new WeakSet();
|
|
530
530
|
var ApprovalCheckPlugin = class extends import_sdk2.DynamicPlugin {
|
|
531
531
|
async checkApproval(flowCtx) {
|
|
532
532
|
const { tool, toolContext } = flowCtx.state;
|
|
@@ -541,17 +541,19 @@ var ApprovalCheckPlugin = class extends import_sdk2.DynamicPlugin {
|
|
|
541
541
|
if (approvalConfig.skipApproval) {
|
|
542
542
|
return;
|
|
543
543
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
passedApprovalStores.set(toolContext, passedStores);
|
|
550
|
-
}
|
|
551
|
-
async enforceApproval(flowCtx, tool, toolContext, approvalConfig, approvalStore) {
|
|
544
|
+
if (decidedCalls.has(toolContext)) return;
|
|
545
|
+
await this.enforceApproval(flowCtx, tool, toolContext, approvalConfig, this.coveringStores(tool));
|
|
546
|
+
decidedCalls.add(toolContext);
|
|
547
|
+
}
|
|
548
|
+
async enforceApproval(flowCtx, tool, toolContext, approvalConfig, stores) {
|
|
552
549
|
const { sessionId, userId } = resolveApprovalIdentity(toolContext.tryGetContext?.());
|
|
553
550
|
const currentContext = this.getCurrentContext(flowCtx);
|
|
554
|
-
const
|
|
551
|
+
const stored = [];
|
|
552
|
+
for (const store of stores) {
|
|
553
|
+
const records2 = await this.readApprovals(store, tool.fullName, sessionId, userId, currentContext);
|
|
554
|
+
stored.push(...records2.map((record) => ({ store, record })));
|
|
555
|
+
}
|
|
556
|
+
const records = stored.map(({ record }) => record);
|
|
555
557
|
if (records.some((record) => record.state === "denied" /* DENIED */)) {
|
|
556
558
|
throw new ApprovalRequiredError({
|
|
557
559
|
toolId: tool.fullName,
|
|
@@ -563,12 +565,13 @@ var ApprovalCheckPlugin = class extends import_sdk2.DynamicPlugin {
|
|
|
563
565
|
return;
|
|
564
566
|
}
|
|
565
567
|
const approved = records.filter((record) => record.state === "approved" /* APPROVED */);
|
|
566
|
-
if (approvalConfig.alwaysPrompt) {
|
|
567
|
-
await this.handleApprovalRequired(flowCtx, approvalConfig, approved[0]);
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
568
|
const now = Date.now();
|
|
571
|
-
|
|
569
|
+
const usable = stored.filter(({ record }) => isApprovalUsable(record, approvalConfig, now));
|
|
570
|
+
if (approvalConfig.alwaysPrompt) {
|
|
571
|
+
for (const { store, record } of usable) {
|
|
572
|
+
if (await this.consumeApproval(store, record, sessionId, userId, currentContext)) return;
|
|
573
|
+
}
|
|
574
|
+
} else if (usable.length > 0) {
|
|
572
575
|
return;
|
|
573
576
|
}
|
|
574
577
|
await this.handleApprovalRequired(
|
|
@@ -577,6 +580,47 @@ var ApprovalCheckPlugin = class extends import_sdk2.DynamicPlugin {
|
|
|
577
580
|
approved.find((record) => isApprovalExpired(record, approvalConfig, now)) ?? approved[0]
|
|
578
581
|
);
|
|
579
582
|
}
|
|
583
|
+
/**
|
|
584
|
+
* The stores of every approval gate that runs for the tool: this plugin's, and that of each other
|
|
585
|
+
* `ApprovalCheckPlugin` whose hook the scope runs for it (an `ApprovalPlugin` on the server and
|
|
586
|
+
* one on the tool's app, say). One store shared by two gates counts once.
|
|
587
|
+
*/
|
|
588
|
+
coveringStores(tool) {
|
|
589
|
+
const stores = /* @__PURE__ */ new Set([this.get(ApprovalStoreToken)]);
|
|
590
|
+
let scope;
|
|
591
|
+
try {
|
|
592
|
+
scope = this.get(import_sdk2.ScopeEntry);
|
|
593
|
+
} catch {
|
|
594
|
+
scope = void 0;
|
|
595
|
+
}
|
|
596
|
+
if (!scope?.hooks) return [...stores];
|
|
597
|
+
for (const hook of scope.hooks.getFlowHooks("tools:call-tool")) {
|
|
598
|
+
const gate = hook.metadata.target;
|
|
599
|
+
if (gate instanceof ApprovalCheckPlugin && (0, import_sdk2.isEntryGatedBy)(scope, { tool }, gate)) {
|
|
600
|
+
stores.add(gate.get(ApprovalStoreToken));
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
return [...stores];
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Use up an approval of an `alwaysPrompt` tool, reporting whether this call got it.
|
|
607
|
+
*
|
|
608
|
+
* `consumeApproval()` deletes exactly that record, and only one of two calls racing for it wins.
|
|
609
|
+
* A store without it has the caller's approvals of the tool revoked instead, which leaves none
|
|
610
|
+
* standing but cannot tell two concurrent calls apart.
|
|
611
|
+
*/
|
|
612
|
+
async consumeApproval(store, record, sessionId, userId, context) {
|
|
613
|
+
if (store.consumeApproval) {
|
|
614
|
+
return store.consumeApproval(record, sessionId, userId, context);
|
|
615
|
+
}
|
|
616
|
+
return store.revokeApproval({
|
|
617
|
+
toolId: record.toolId,
|
|
618
|
+
sessionId,
|
|
619
|
+
userId,
|
|
620
|
+
revokedBy: "system",
|
|
621
|
+
reason: "Used by a call of an alwaysPrompt tool"
|
|
622
|
+
});
|
|
623
|
+
}
|
|
580
624
|
/**
|
|
581
625
|
* The caller's approvals and denials of the tool, from the context it runs in too. Stores
|
|
582
626
|
* without `getApprovals()` give the one record `getApproval()` picks.
|
|
@@ -1007,6 +1051,9 @@ var import_utils3 = require("@frontmcp/utils");
|
|
|
1007
1051
|
function escapePattern(str) {
|
|
1008
1052
|
return str.replace(/[*?[\]\\]/g, "\\$&");
|
|
1009
1053
|
}
|
|
1054
|
+
function isSameRecord(a, b) {
|
|
1055
|
+
return a.toolId === b.toolId && a.state === b.state && a.scope === b.scope && a.grantedAt === b.grantedAt && a.sessionId === b.sessionId && a.userId === b.userId && a.context?.type === b.context?.type && a.context?.identifier === b.context?.identifier;
|
|
1056
|
+
}
|
|
1010
1057
|
var REVOCATION_RETENTION_SECONDS = 24 * 60 * 60;
|
|
1011
1058
|
var TIME_LIMITED_KEY_SEGMENT = `scope:${"time_limited" /* TIME_LIMITED */}`;
|
|
1012
1059
|
var ApprovalStorageStore = class {
|
|
@@ -1244,6 +1291,31 @@ var ApprovalStorageStore = class {
|
|
|
1244
1291
|
await this.storage.mdelete(keysToDelete);
|
|
1245
1292
|
return true;
|
|
1246
1293
|
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Deletes the key that holds `record`, if it still does, and reports whether this call deleted
|
|
1296
|
+
* it. Two calls racing for one approval both find it, but only one deletes it, and a denial or
|
|
1297
|
+
* approval written to the key after it was read is kept.
|
|
1298
|
+
*/
|
|
1299
|
+
async consumeApproval(record, sessionId, userId, context) {
|
|
1300
|
+
this.ensureInitialized();
|
|
1301
|
+
for (const key of this.callerKeys(record.toolId, sessionId, userId, context)) {
|
|
1302
|
+
const storedValue = await this.storage.get(key);
|
|
1303
|
+
const stored = this.parseRecord(storedValue);
|
|
1304
|
+
if (storedValue !== null && stored && isSameRecord(stored, record)) {
|
|
1305
|
+
return this.deleteIfUnchanged(key, storedValue);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
return false;
|
|
1309
|
+
}
|
|
1310
|
+
/** Deletes `key` only while it still holds `storedValue`, in one step where the backend allows. */
|
|
1311
|
+
async deleteIfUnchanged(key, storedValue) {
|
|
1312
|
+
try {
|
|
1313
|
+
return await this.storage.deleteIfEquals(key, storedValue);
|
|
1314
|
+
} catch (error) {
|
|
1315
|
+
if (!(error instanceof import_utils3.StorageNotSupportedError)) throw error;
|
|
1316
|
+
return this.storage.delete(key);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1247
1319
|
async getRevocations(toolId, sessionId, userId, context) {
|
|
1248
1320
|
this.ensureInitialized();
|
|
1249
1321
|
const records = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugin-approval",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Approval plugin for FrontMCP - tool authorization workflow with PKCE webhook security",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -49,13 +49,13 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@frontmcp/sdk": "1.
|
|
53
|
-
"@frontmcp/utils": "1.
|
|
52
|
+
"@frontmcp/sdk": "1.9.0",
|
|
53
|
+
"@frontmcp/utils": "1.9.0",
|
|
54
54
|
"ioredis": "^5.8.0",
|
|
55
55
|
"reflect-metadata": "^0.2.2"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@frontmcp/lazy-zod": "1.
|
|
58
|
+
"@frontmcp/lazy-zod": "1.9.0",
|
|
59
59
|
"@vercel/kv": "^2.0.0 || ^3.0.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependenciesMeta": {
|
|
@@ -88,6 +88,14 @@ export declare class ApprovalStorageStore implements ApprovalStore {
|
|
|
88
88
|
* (session, user, time-limited and context approvals alike). Recorded denials are kept.
|
|
89
89
|
*/
|
|
90
90
|
revokeApproval(options: RevokeApprovalOptions): Promise<boolean>;
|
|
91
|
+
/**
|
|
92
|
+
* Deletes the key that holds `record`, if it still does, and reports whether this call deleted
|
|
93
|
+
* it. Two calls racing for one approval both find it, but only one deletes it, and a denial or
|
|
94
|
+
* approval written to the key after it was read is kept.
|
|
95
|
+
*/
|
|
96
|
+
consumeApproval(record: ApprovalRecord, sessionId: string, userId?: string, context?: ApprovalContext): Promise<boolean>;
|
|
97
|
+
/** Deletes `key` only while it still holds `storedValue`, in one step where the backend allows. */
|
|
98
|
+
private deleteIfUnchanged;
|
|
91
99
|
getRevocations(toolId: string, sessionId: string, userId?: string, context?: ApprovalContext): Promise<ApprovalRecord[]>;
|
|
92
100
|
isApproved(toolId: string, sessionId: string, userId?: string, context?: ApprovalContext): Promise<boolean>;
|
|
93
101
|
clearSessionApprovals(sessionId: string): Promise<number>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-storage.store.d.ts","sourceRoot":"","sources":["../../src/stores/approval-storage.store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,
|
|
1
|
+
{"version":3,"file":"approval-storage.store.d.ts","sourceRoot":"","sources":["../../src/stores/approval-storage.store.ts"],"names":[],"mappings":"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"}
|
|
@@ -101,6 +101,15 @@ export interface ApprovalStore {
|
|
|
101
101
|
* (only the given context's, when `context` is set). Recorded denials are kept.
|
|
102
102
|
*/
|
|
103
103
|
revokeApproval(options: RevokeApprovalOptions): Promise<boolean>;
|
|
104
|
+
/**
|
|
105
|
+
* Use up one approval: delete the stored record `record` (one `getApprovals()` returned for this
|
|
106
|
+
* caller) and resolve `true` only for the call that deleted it, so two calls racing for one
|
|
107
|
+
* approval cannot both get it. A record written in its place since it was read (a denial, a new
|
|
108
|
+
* approval) must be kept, so the delete has to compare and delete in one step. The approval gate uses it for `alwaysPrompt` tools, where each
|
|
109
|
+
* approval admits a single call. Optional: for a store without it the gate revokes the caller's
|
|
110
|
+
* approvals of the tool instead.
|
|
111
|
+
*/
|
|
112
|
+
consumeApproval?(record: ApprovalRecord, sessionId: string, userId?: string, context?: ApprovalContext): Promise<boolean>;
|
|
104
113
|
/**
|
|
105
114
|
* The caller's recent revocations of a tool: each is the approval that was revoked, with `revokedBy`,
|
|
106
115
|
* `revokedAt` and `revocationReason`. Optional: a store that keeps no revocations leaves it out.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-store.interface.d.ts","sourceRoot":"","sources":["../../src/stores/approval-store.interface.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"approval-store.interface.d.ts","sourceRoot":"","sources":["../../src/stores/approval-store.interface.ts"],"names":[],"mappings":"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"}
|