@webdecoy/ai-protection 0.1.0-alpha.18 → 0.1.0-alpha.19
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/MCP.md +7 -1
- package/actions.d.mts +1 -1
- package/actions.mjs +3 -1
- package/mcp.mjs +10 -3
- package/package.json +1 -1
package/MCP.md
CHANGED
|
@@ -11,7 +11,7 @@ resource authorization.
|
|
|
11
11
|
Available in `0.1.0-alpha.5` and later compatible alpha releases:
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
|
-
npm install @webdecoy/ai-protection@0.1.0-alpha.
|
|
14
|
+
npm install @webdecoy/ai-protection@0.1.0-alpha.19 @modelcontextprotocol/sdk@1.31.0
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Requires Node 22.22.3+ and MCP SDK **1.31.0**. The MCP SDK is an optional peer, so
|
|
@@ -115,6 +115,12 @@ have unknown work. Never retry writes without application/provider idempotency.
|
|
|
115
115
|
for its runtime prerequisite and application-enforced bounds. Full product
|
|
116
116
|
acceptance remains separate from this adapter's tested contract.
|
|
117
117
|
|
|
118
|
+
From alpha.19, every tool result also carries `_meta["webdecoy.com/action"].actionId`:
|
|
119
|
+
allowed results (merged with your own `_meta`), denials and failed callbacks alike.
|
|
120
|
+
It is the action ID on that call's reported evidence, so the caller or an operator
|
|
121
|
+
can find the exact action in the dashboard. It is a random identifier per call and
|
|
122
|
+
carries no identity, arguments or result content.
|
|
123
|
+
|
|
118
124
|
This is an explicit tools integration, not transparent protection of an existing
|
|
119
125
|
whole MCP server. See the [MCP transport specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports)
|
|
120
126
|
and [authorization specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization).
|
package/actions.d.mts
CHANGED
|
@@ -115,4 +115,4 @@ export function createActionProtection<T>(options: {
|
|
|
115
115
|
actions: Record<string, ActionDefinition>;
|
|
116
116
|
/** Local best-effort observer. sharedRuntime enables independent hosted reporting. */
|
|
117
117
|
onEvent?(event: ActionEvent): void | Promise<void>;
|
|
118
|
-
}): {flush():Promise<void>;run(action: string, args: ActionInput, authenticationContext: T, options?: {signal?: AbortSignal}): Promise<unknown>};
|
|
118
|
+
}): {flush():Promise<void>;run(action: string, args: ActionInput, authenticationContext: T, options?: {signal?: AbortSignal; /** Receives this invocation's action ID before any check runs. */ onAction?(actionId: string): void}): Promise<unknown>};
|
package/actions.mjs
CHANGED
|
@@ -82,8 +82,10 @@ export function createActionProtection(options) {
|
|
|
82
82
|
if (!actions.size || actions.size > 128) throw Error('Expected 1–128 actions');
|
|
83
83
|
const runtime=prepareActionRuntime(options,actions);
|
|
84
84
|
let pendingEvents = 0;
|
|
85
|
-
return Object.freeze({flush:async()=>{await runtime?.flush();},async run(name, input, authenticationContext, {signal} = {}) {
|
|
85
|
+
return Object.freeze({flush:async()=>{await runtime?.flush();},async run(name, input, authenticationContext, {signal, onAction} = {}) {
|
|
86
86
|
const actionId = randomUUID();
|
|
87
|
+
// Lets an adapter return the action ID to its caller; never affects the decision.
|
|
88
|
+
try { onAction?.(actionId); } catch {}
|
|
87
89
|
// Unknown caller-controlled action strings are never placed in evidence.
|
|
88
90
|
const action = actions.get(name), eventAction = action ? name : 'unregistered';
|
|
89
91
|
let attempted = false,completed=false,lease,work,callerEvidence;
|
package/mcp.mjs
CHANGED
|
@@ -227,16 +227,23 @@ export function createProtectedMCPHandler(options) {
|
|
|
227
227
|
throw new McpError(ErrorCode.InvalidParams, 'Tool unavailable');
|
|
228
228
|
running.started = true;
|
|
229
229
|
const signal = AbortSignal.any([extra.signal, disconnected.signal, running.controller.signal]);
|
|
230
|
+
// The action ID matches this call's reported evidence, so the caller can find it in the dashboard.
|
|
231
|
+
let actionId;
|
|
232
|
+
const action = () => ({ 'webdecoy.com/action': { actionId } });
|
|
230
233
|
try {
|
|
231
|
-
|
|
234
|
+
const result = await guard.run(request.params.name, (request.params.arguments ?? {}), caller, { signal, onAction: id => { actionId = id; } });
|
|
235
|
+
if (!result || typeof result !== 'object' || Array.isArray(result))
|
|
236
|
+
return result;
|
|
237
|
+
const meta = result._meta && typeof result._meta === 'object' && !Array.isArray(result._meta) ? result._meta : {};
|
|
238
|
+
return { ...result, _meta: { ...meta, ...action() } };
|
|
232
239
|
}
|
|
233
240
|
catch (e) {
|
|
234
241
|
if (signal.aborted)
|
|
235
242
|
throw new McpError(ErrorCode.InternalError, 'Request cancelled');
|
|
236
243
|
if (e instanceof ActionDenied)
|
|
237
244
|
return { isError: true, content: [{ type: 'text', text: `Action denied: ${e.reason}` }],
|
|
238
|
-
_meta: { 'webdecoy.com/action-error': { reason: e.reason, status: e.status, ...(e.retryAfterSeconds ? { retryAfterSeconds: e.retryAfterSeconds } : {}) } } };
|
|
239
|
-
return { isError: true, content: [{ type: 'text', text: 'Action failed; outcome may be unknown' }] };
|
|
245
|
+
_meta: { ...action(), 'webdecoy.com/action-error': { reason: e.reason, status: e.status, ...(e.retryAfterSeconds ? { retryAfterSeconds: e.retryAfterSeconds } : {}) } } };
|
|
246
|
+
return { isError: true, content: [{ type: 'text', text: 'Action failed; outcome may be unknown' }], _meta: action() };
|
|
240
247
|
}
|
|
241
248
|
finally {
|
|
242
249
|
cleanup();
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"test:types": "tsc --strict --noEmit --module nodenext --target es2022 test/types.mts test/mcp-types.mts",
|
|
12
12
|
"test:workers": "node --test test/workers/*.test.mjs"
|
|
13
13
|
},
|
|
14
|
-
"version": "0.1.0-alpha.
|
|
14
|
+
"version": "0.1.0-alpha.19",
|
|
15
15
|
"exports": {
|
|
16
16
|
"./fetch": {
|
|
17
17
|
"types": "./fetch.d.mts",
|