ue-mcp 1.2.0 → 1.2.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.
- package/dist/flow/guard.d.ts +26 -37
- package/dist/flow/guard.js +40 -25
- package/dist/flow/guard.js.map +1 -1
- package/dist/flow/guarded-bridge.d.ts +0 -10
- package/dist/flow/guarded-bridge.js +18 -23
- package/dist/flow/guarded-bridge.js.map +1 -1
- package/dist/flow/schema.d.ts +276 -16
- package/dist/flow/task-guards.d.ts +1 -1
- package/dist/flow/task-guards.js +44 -68
- package/dist/flow/task-guards.js.map +1 -1
- package/dist/tool-counts.json +2 -2
- package/package.json +7 -4
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +10 -32
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -21
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +3 -18
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +1 -19
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerUtils.h +30 -0
package/dist/flow/guard.d.ts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* guards/interceptors but agnostic to what any guard does.
|
|
2
|
+
* The bridge's binding of flowkit's guard pipeline.
|
|
4
3
|
*
|
|
5
|
-
* Every mutating and non-mutating action crosses `IBridge.call`.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* policy, rate limiting, or any concrete concern - those are guards.
|
|
4
|
+
* Every mutating and non-mutating action crosses `IBridge.call`. That seam is
|
|
5
|
+
* guarded: each guard may inspect the call, run a `before` hook that can veto
|
|
6
|
+
* it (throw) or act on it (e.g. check a file out), and an `after` hook that can
|
|
7
|
+
* observe or replace the result. The chain itself knows nothing about source
|
|
8
|
+
* control, policy, rate limiting, or any concrete concern - those are guards.
|
|
11
9
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* The pipeline, its ordering, and the `guard.<name>.<phase>` task convention
|
|
11
|
+
* live in `@db-lyon/flowkit/guard`, because none of that is about Unreal. What
|
|
12
|
+
* stays here is what is: the shape of a bridge call, and the write
|
|
13
|
+
* classification in `write-methods.ts` that decides which content paths a call
|
|
14
|
+
* is about to modify.
|
|
16
15
|
*/
|
|
16
|
+
import { GuardRegistry as FlowkitGuardRegistry, type Guard, type GuardContext } from "@db-lyon/flowkit/guard";
|
|
17
17
|
import type { IBridge } from "../bridge.js";
|
|
18
18
|
import type { EditorSession } from "../session.js";
|
|
19
19
|
import { type WriteClassification } from "./write-methods.js";
|
|
20
20
|
/** Resolve a UE content path to an absolute on-disk file, or null if it does not exist. */
|
|
21
21
|
export type ResolveExistingFile = (contentPath: string) => string | null;
|
|
22
|
-
/** Per-call execution context passed to every guard.
|
|
23
|
-
export interface CallContext {
|
|
22
|
+
/** Per-call execution context passed to every guard. */
|
|
23
|
+
export interface CallContext extends GuardContext {
|
|
24
24
|
readonly method: string;
|
|
25
25
|
readonly params: Record<string, unknown>;
|
|
26
26
|
readonly timeoutMs?: number;
|
|
@@ -28,36 +28,25 @@ export interface CallContext {
|
|
|
28
28
|
readonly bridge: IBridge;
|
|
29
29
|
/** The editor this call is bound to. Absent only for a bridge built outside a session. */
|
|
30
30
|
readonly session?: EditorSession;
|
|
31
|
-
/** Scratch space shared across guards for the life of one call. */
|
|
32
|
-
readonly meta: Map<string, unknown>;
|
|
33
31
|
/** Lazy: how this call classifies as a write (which content paths it touches). Cached. */
|
|
34
32
|
write(): WriteClassification;
|
|
35
33
|
/** Lazy: absolute, existing on-disk files this call will modify (subset of write paths). Cached. */
|
|
36
34
|
writeFiles(): string[];
|
|
37
35
|
}
|
|
38
36
|
/**
|
|
39
|
-
* A guard on the bridge pipeline.
|
|
40
|
-
*
|
|
41
|
-
* gating are all just guards.
|
|
37
|
+
* A guard on the bridge pipeline. Guards are agnostic: source control, access
|
|
38
|
+
* policy, audit, rate limiting, and approval gating are all just guards.
|
|
42
39
|
*/
|
|
43
|
-
export
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/** Lower runs first in `before` and last in `after`. Default 0. */
|
|
47
|
-
readonly order?: number;
|
|
48
|
-
/** Whether this guard participates for a given call. Default: always. */
|
|
49
|
-
appliesTo?(ctx: CallContext): boolean | Promise<boolean>;
|
|
50
|
-
/** Runs before the call. Throw to DENY the call; side effects are allowed. */
|
|
51
|
-
before?(ctx: CallContext): Promise<void>;
|
|
52
|
-
/** Runs after a successful call. Return a value to replace the result; return nothing to leave it. */
|
|
53
|
-
after?(ctx: CallContext, result: unknown): Promise<unknown | void>;
|
|
54
|
-
}
|
|
55
|
-
/** An ordered set of guards. Built-in guards register directly; plugin guards are discovered. */
|
|
56
|
-
export declare class GuardRegistry {
|
|
57
|
-
private guards;
|
|
58
|
-
register(guard: BridgeGuard): this;
|
|
59
|
-
list(): readonly BridgeGuard[];
|
|
60
|
-
get size(): number;
|
|
40
|
+
export type BridgeGuard = Guard<CallContext, unknown>;
|
|
41
|
+
/** The bridge's guard set. Built-in guards register directly; plugin guards are discovered. */
|
|
42
|
+
export declare class GuardRegistry extends FlowkitGuardRegistry<CallContext, unknown> {
|
|
61
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* The scope a `guard.<name>.<phase>Write` task binds to: the call resolves to
|
|
46
|
+
* existing on-disk files it is about to modify. Declared here rather than in
|
|
47
|
+
* the task layer so the hand-written and task-backed guards agree on what
|
|
48
|
+
* "write" means.
|
|
49
|
+
*/
|
|
50
|
+
export declare function writeScope(ctx: CallContext): boolean;
|
|
62
51
|
/** Build the per-call context, wiring the lazy write-enrichment helpers. */
|
|
63
52
|
export declare function makeCallContext(method: string, params: Record<string, unknown>, timeoutMs: number | undefined, rawBridge: IBridge, resolveExistingFile: ResolveExistingFile, session?: EditorSession): CallContext;
|
package/dist/flow/guard.js
CHANGED
|
@@ -1,34 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bridge's binding of flowkit's guard pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Every mutating and non-mutating action crosses `IBridge.call`. That seam is
|
|
5
|
+
* guarded: each guard may inspect the call, run a `before` hook that can veto
|
|
6
|
+
* it (throw) or act on it (e.g. check a file out), and an `after` hook that can
|
|
7
|
+
* observe or replace the result. The chain itself knows nothing about source
|
|
8
|
+
* control, policy, rate limiting, or any concrete concern - those are guards.
|
|
9
|
+
*
|
|
10
|
+
* The pipeline, its ordering, and the `guard.<name>.<phase>` task convention
|
|
11
|
+
* live in `@db-lyon/flowkit/guard`, because none of that is about Unreal. What
|
|
12
|
+
* stays here is what is: the shape of a bridge call, and the write
|
|
13
|
+
* classification in `write-methods.ts` that decides which content paths a call
|
|
14
|
+
* is about to modify.
|
|
15
|
+
*/
|
|
16
|
+
import { GuardRegistry as FlowkitGuardRegistry, guardContextBase, lazy, } from "@db-lyon/flowkit/guard";
|
|
1
17
|
import { classifyWrite } from "./write-methods.js";
|
|
2
|
-
/**
|
|
3
|
-
export class GuardRegistry {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
get size() {
|
|
15
|
-
return this.guards.length;
|
|
16
|
-
}
|
|
18
|
+
/** The bridge's guard set. Built-in guards register directly; plugin guards are discovered. */
|
|
19
|
+
export class GuardRegistry extends FlowkitGuardRegistry {
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The scope a `guard.<name>.<phase>Write` task binds to: the call resolves to
|
|
23
|
+
* existing on-disk files it is about to modify. Declared here rather than in
|
|
24
|
+
* the task layer so the hand-written and task-backed guards agree on what
|
|
25
|
+
* "write" means.
|
|
26
|
+
*/
|
|
27
|
+
export function writeScope(ctx) {
|
|
28
|
+
return ctx.writeFiles().length > 0;
|
|
17
29
|
}
|
|
18
30
|
/** Build the per-call context, wiring the lazy write-enrichment helpers. */
|
|
19
31
|
export function makeCallContext(method, params, timeoutMs, rawBridge, resolveExistingFile, session) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
const ctx = {
|
|
33
|
+
...guardContextBase(),
|
|
34
|
+
method,
|
|
35
|
+
params,
|
|
36
|
+
timeoutMs,
|
|
37
|
+
bridge: rawBridge,
|
|
38
|
+
session,
|
|
39
|
+
};
|
|
40
|
+
const write = lazy(ctx, "write", () => classifyWrite(method, params));
|
|
41
|
+
const writeFiles = lazy(ctx, "writeFiles", () => {
|
|
26
42
|
const c = write();
|
|
27
|
-
|
|
43
|
+
return c.writes
|
|
28
44
|
? c.contentPaths.map(resolveExistingFile).filter((f) => f !== null)
|
|
29
45
|
: [];
|
|
30
|
-
|
|
31
|
-
};
|
|
32
|
-
return { method, params, timeoutMs, bridge: rawBridge, session, meta: new Map(), write, writeFiles };
|
|
46
|
+
});
|
|
47
|
+
return Object.assign(ctx, { write, writeFiles });
|
|
33
48
|
}
|
|
34
49
|
//# sourceMappingURL=guard.js.map
|
package/dist/flow/guard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/flow/guard.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/flow/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EACL,aAAa,IAAI,oBAAoB,EACrC,gBAAgB,EAChB,IAAI,GAGL,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAAE,aAAa,EAA4B,MAAM,oBAAoB,CAAC;AA0B7E,+FAA+F;AAC/F,MAAM,OAAO,aAAc,SAAQ,oBAA0C;CAAG;AAEhF;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,GAAgB;IACzC,OAAO,GAAG,CAAC,UAAU,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;AACrC,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAC7B,MAAc,EACd,MAA+B,EAC/B,SAA6B,EAC7B,SAAkB,EAClB,mBAAwC,EACxC,OAAuB;IAEvB,MAAM,GAAG,GAAG;QACV,GAAG,gBAAgB,EAAE;QACrB,MAAM;QACN,MAAM;QACN,SAAS;QACT,MAAM,EAAE,SAAS;QACjB,OAAO;KACO,CAAC;IAEjB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,GAAG,KAAK,EAAE,CAAC;QAClB,OAAO,CAAC,CAAC,MAAM;YACb,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;YAChF,CAAC,CAAC,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GuardedBridge - runs a `GuardRegistry` pipeline around the editor bridge.
|
|
3
|
-
*
|
|
4
|
-
* Before a call reaches Unreal, each applicable guard's `before` hook runs in
|
|
5
|
-
* order (any may throw to deny). After a successful call, each guard's `after`
|
|
6
|
-
* hook runs in reverse order and may replace the result. With an empty registry
|
|
7
|
-
* this is a pure pass-through, so it is always safe to install.
|
|
8
|
-
*
|
|
9
|
-
* Only `call` is gated; connection lifecycle delegates straight through.
|
|
10
|
-
*/
|
|
11
1
|
import type { BridgeTarget, IBridge } from "../bridge.js";
|
|
12
2
|
import type { EditorSession } from "../session.js";
|
|
13
3
|
import { GuardRegistry, type ResolveExistingFile } from "./guard.js";
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* GuardedBridge - runs a `GuardRegistry` pipeline around the editor bridge.
|
|
3
|
+
*
|
|
4
|
+
* Before a call reaches Unreal, each applicable guard's `before` hook runs in
|
|
5
|
+
* order (any may throw to deny). After a successful call, each guard's `after`
|
|
6
|
+
* hook runs in reverse order and may replace the result. With an empty registry
|
|
7
|
+
* this is a pure pass-through, so it is always safe to install.
|
|
8
|
+
*
|
|
9
|
+
* The pipeline itself is `runGuarded` from flowkit; what this class adds is the
|
|
10
|
+
* `IBridge` shape. Only `call` is gated; connection lifecycle delegates
|
|
11
|
+
* straight through.
|
|
12
|
+
*/
|
|
13
|
+
import { runGuarded } from "@db-lyon/flowkit/guard";
|
|
14
|
+
import { makeCallContext } from "./guard.js";
|
|
2
15
|
export class GuardedBridge {
|
|
3
16
|
inner;
|
|
4
17
|
registry;
|
|
@@ -26,32 +39,14 @@ export class GuardedBridge {
|
|
|
26
39
|
return this.inner.getTarget();
|
|
27
40
|
}
|
|
28
41
|
async call(method, params, timeoutMs) {
|
|
42
|
+
// `runGuarded` is already a pass-through on an empty registry, but building
|
|
43
|
+
// the context is not free and every bridge call lands here. Most servers run
|
|
44
|
+
// with no guards at all, so skip the allocation outright.
|
|
29
45
|
if (this.registry.size === 0) {
|
|
30
46
|
return this.inner.call(method, params, timeoutMs);
|
|
31
47
|
}
|
|
32
48
|
const ctx = makeCallContext(method, params ?? {}, timeoutMs, this.inner, this.resolveExistingFile, this.session);
|
|
33
|
-
|
|
34
|
-
const applicable = [];
|
|
35
|
-
for (const g of this.registry.list()) {
|
|
36
|
-
if (!g.appliesTo || (await g.appliesTo(ctx)))
|
|
37
|
-
applicable.push(g);
|
|
38
|
-
}
|
|
39
|
-
// before: in order. A throw denies the call and propagates unchanged.
|
|
40
|
-
for (const g of applicable) {
|
|
41
|
-
if (g.before)
|
|
42
|
-
await g.before(ctx);
|
|
43
|
-
}
|
|
44
|
-
let result = await this.inner.call(method, params, timeoutMs);
|
|
45
|
-
// after: in reverse order. A returned value replaces the result.
|
|
46
|
-
for (let i = applicable.length - 1; i >= 0; i--) {
|
|
47
|
-
const g = applicable[i];
|
|
48
|
-
if (g.after) {
|
|
49
|
-
const replaced = await g.after(ctx, result);
|
|
50
|
-
if (replaced !== undefined)
|
|
51
|
-
result = replaced;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return result;
|
|
49
|
+
return runGuarded(ctx, this.registry, () => this.inner.call(method, params, timeoutMs));
|
|
55
50
|
}
|
|
56
51
|
}
|
|
57
52
|
//# sourceMappingURL=guarded-bridge.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guarded-bridge.js","sourceRoot":"","sources":["../../src/flow/guarded-bridge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"guarded-bridge.js","sourceRoot":"","sources":["../../src/flow/guarded-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGpD,OAAO,EAAiB,eAAe,EAA4B,MAAM,YAAY,CAAC;AAItF,MAAM,OAAO,aAAa;IAEL;IACA;IACA;IAGA;IANnB,YACmB,KAAc,EACd,QAAuB,EACvB,mBAAwC;IACzD;2EACuE;IACtD,OAAuB;QALvB,UAAK,GAAL,KAAK,CAAS;QACd,aAAQ,GAAR,QAAQ,CAAe;QACvB,wBAAmB,GAAnB,mBAAmB,CAAqB;QAGxC,YAAO,GAAP,OAAO,CAAgB;IACvC,CAAC;IAEJ,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;IAChC,CAAC;IAED,OAAO,CAAC,SAAkB;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,eAAe,CAAC,YAAoB,EAAE,UAAmB;QACvD,OAAO,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAC9D,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,IAAI,CACR,MAAc,EACd,MAAgC,EAChC,SAAkB;QAElB,4EAA4E;QAC5E,6EAA6E;QAC7E,0DAA0D;QAC1D,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,GAAG,GAAG,eAAe,CACzB,MAAM,EACN,MAAM,IAAI,EAAE,EACZ,SAAS,EACT,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,mBAAmB,EACxB,IAAI,CAAC,OAAO,CACb,CAAC;QACF,OAAO,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAC1F,CAAC;CACF"}
|