@ory/openclaw 1.5.3 → 1.6.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/README.md +23 -1
- package/dist/plugin.js +56 -9
- package/dist/types.d.ts +49 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -102,6 +102,28 @@ npx -y -p @ory/openclaw ory-openclaw permissions # native allowed/blocked stat
|
|
|
102
102
|
|
|
103
103
|
Then a denied tool is actually blocked and OpenClaw shows why.
|
|
104
104
|
|
|
105
|
+
### MCP servers
|
|
106
|
+
|
|
107
|
+
OpenClaw exposes a bundle MCP server's tools as `<server>__<tool>`, where the
|
|
108
|
+
server part is the configured name with characters outside `[A-Za-z0-9_-]`
|
|
109
|
+
replaced by `-`, cut to 30 characters, and suffixed `-2`, `-3`, … on a
|
|
110
|
+
collision (OpenClaw v2026.10.6, `src/agents/agent-bundle-mcp-names.ts`). The
|
|
111
|
+
plugin splits a tool name on its first `__` and checks `McpServer:<server>` and
|
|
112
|
+
`McpTool:<server>:<tool>`; Agent Security resolves the reported name to a
|
|
113
|
+
cataloged server. OpenClaw's built-in tools are not named with `__`, so they
|
|
114
|
+
keep the native check.
|
|
115
|
+
|
|
116
|
+
Limits: OpenClaw reports neither the configured server key nor how the server
|
|
117
|
+
is launched, so no fingerprints are sent, and a renamed, truncated, or
|
|
118
|
+
collision-suffixed server name may resolve as uncatalogued. A server name that
|
|
119
|
+
itself contains `__` splits at the wrong place. MCP calls made through
|
|
120
|
+
OpenClaw's code mode arrive as one code-mode tool and are not identified.
|
|
121
|
+
|
|
122
|
+
The tool hooks register through `api.on`. Current OpenClaw only invokes typed
|
|
123
|
+
hooks such as `before_tool_call` that way and ignores `registerHook`
|
|
124
|
+
registrations for them. On hosts without `api.on`, the plugin falls back to
|
|
125
|
+
`registerHook`.
|
|
126
|
+
|
|
105
127
|
## Connect to Ory Agent Security
|
|
106
128
|
|
|
107
129
|
Copy the connection details from the [Ory Console](https://console.ory.sh) under **Agent Security**:
|
|
@@ -122,7 +144,7 @@ npx -y -p @ory/openclaw ory-openclaw configure \
|
|
|
122
144
|
|
|
123
145
|
**Ory Network or OEL.** Either works. For an Ory Network project the URL is `https://<slug>.projects.oryapis.com`; for a self-hosted **Ory Enterprise License** deployment, point `--project-url` at that deployment's base URL and use the sign-in client id from its Agent Security configuration. Everything downstream — sign-in, checks, delegation — is identical.
|
|
124
146
|
|
|
125
|
-
**There is nothing else for you to create.** The sign-in client, permission model, native-tool blocks, MCP
|
|
147
|
+
**There is nothing else for you to create.** The sign-in client, permission model, native-tool blocks, MCP server access, and observe/enforce posture are provisioned in the Console by someone with project access. At session start the plugin loads or enrolls OpenClaw's Talos runtime credential in the OS credential store; tool paths only read it. Installing needs no workspace privilege.
|
|
126
148
|
|
|
127
149
|
Sign-in runs at the start of every session and never blocks — a declined, skipped, or timed-out login simply leaves that session without a user identity.
|
|
128
150
|
|
package/dist/plugin.js
CHANGED
|
@@ -22,14 +22,23 @@ function createOryPlugin(clientOrConfig, deps = {}) {
|
|
|
22
22
|
name: "ory.user-login",
|
|
23
23
|
description: "Block runs when the user login denies",
|
|
24
24
|
});
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
const beforeToolCall = createBeforeToolCallHandler(client, subagents);
|
|
26
|
+
const afterToolCall = createAfterToolCallHandler(client);
|
|
27
|
+
if (typeof api.on === "function") {
|
|
28
|
+
// Typed tool hooks only fire through `api.on` on current OpenClaw.
|
|
29
|
+
api.on("before_tool_call", (event, ctx) => beforeToolCall(fromTypedToolEvent(event, ctx)));
|
|
30
|
+
api.on("after_tool_call", (event, ctx) => afterToolCall(fromTypedAfterToolEvent(event, ctx)));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
api.registerHook("before_tool_call", beforeToolCall, {
|
|
34
|
+
name: "ory.permission-check",
|
|
35
|
+
description: "Check tool permissions via Ory Permissions",
|
|
36
|
+
});
|
|
37
|
+
api.registerHook("after_tool_call", afterToolCall, {
|
|
38
|
+
name: "ory.activity-complete",
|
|
39
|
+
description: "Record tool completion activity",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
33
42
|
api.registerHook("subagent_spawning", createSubagentSpawningHandler(client, deps, subagents), {
|
|
34
43
|
name: "ory.subagent-register",
|
|
35
44
|
description: "Register Ory identity and write delegation tuple for spawned sub-agent",
|
|
@@ -74,7 +83,7 @@ function createBeforeToolCallHandler(client, subagents) {
|
|
|
74
83
|
toolArgs: ctx.args,
|
|
75
84
|
subjectFallback: ctx.sessionId ? `session:${ctx.sessionId}` : "agent:openclaw",
|
|
76
85
|
canBlock: true,
|
|
77
|
-
mcpTool: (
|
|
86
|
+
mcpTool: identifyMcpTool(ctx.toolId),
|
|
78
87
|
principals: openClawSubagentPrincipals(ctx, subagents),
|
|
79
88
|
});
|
|
80
89
|
if (result.blocked) {
|
|
@@ -82,6 +91,44 @@ function createBeforeToolCallHandler(client, subagents) {
|
|
|
82
91
|
}
|
|
83
92
|
});
|
|
84
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* OpenClaw names a bundle MCP tool `<safeServer>__<tool>`
|
|
96
|
+
* (`src/agents/agent-bundle-mcp-names.ts`): the configured server name with
|
|
97
|
+
* characters outside `[A-Za-z0-9_-]` replaced by `-`, at most 30 characters,
|
|
98
|
+
* suffixed `-2`, `-3`, … on collisions. The safe name is not the config key
|
|
99
|
+
* when sanitizing, truncation, or a collision changed it, so the first `__`
|
|
100
|
+
* splits the name instead of a configured-key lookup; Agent Security folds
|
|
101
|
+
* separators when resolving the reported name. Native OpenClaw tools carry
|
|
102
|
+
* no `__`.
|
|
103
|
+
*/
|
|
104
|
+
function identifyMcpTool(toolName) {
|
|
105
|
+
return (0, argus_1.parseMcpToolWithSeparator)(toolName, "__") ?? undefined;
|
|
106
|
+
}
|
|
107
|
+
/** Map a typed `before_tool_call` (event, ctx) pair onto the handler context. */
|
|
108
|
+
function fromTypedToolEvent(event, ctx = {}) {
|
|
109
|
+
return {
|
|
110
|
+
toolId: event.toolName,
|
|
111
|
+
toolName: event.toolName,
|
|
112
|
+
args: event.params ?? {},
|
|
113
|
+
sessionId: ctx.sessionId ?? ctx.sessionKey ?? "",
|
|
114
|
+
callId: event.toolCallId ?? ctx.toolCallId ?? "",
|
|
115
|
+
agentId: ctx.agentId,
|
|
116
|
+
sessionKey: ctx.sessionKey,
|
|
117
|
+
runId: event.runId ?? ctx.runId,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function fromTypedAfterToolEvent(event, ctx = {}) {
|
|
121
|
+
return {
|
|
122
|
+
toolId: event.toolName,
|
|
123
|
+
toolName: event.toolName,
|
|
124
|
+
args: event.params ?? {},
|
|
125
|
+
result: event.result,
|
|
126
|
+
sessionId: ctx.sessionId ?? ctx.sessionKey ?? "",
|
|
127
|
+
callId: event.toolCallId ?? ctx.toolCallId ?? "",
|
|
128
|
+
durationMs: event.durationMs ?? 0,
|
|
129
|
+
...(event.error ? { error: event.error } : {}),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
85
132
|
function openClawSubagentPrincipals(ctx, subagents) {
|
|
86
133
|
const childSession = ctx.sessionKey ?? ctx.sessionId;
|
|
87
134
|
const subagent = subagents.get(childSession);
|
package/dist/types.d.ts
CHANGED
|
@@ -19,6 +19,54 @@ export interface OpenClawPluginEntry {
|
|
|
19
19
|
}
|
|
20
20
|
export interface OpenClawPluginApi {
|
|
21
21
|
registerHook<E extends HookEvent>(event: E, handler: HookHandler<E>, opts?: HookRegistrationOpts): void;
|
|
22
|
+
/**
|
|
23
|
+
* Register a typed lifecycle hook. Current OpenClaw dispatches typed names
|
|
24
|
+
* such as `before_tool_call` only to `api.on` registrations and warns that
|
|
25
|
+
* `registerHook` registrations for them are never invoked
|
|
26
|
+
* (`src/plugins/registry-registrars-tools-hooks.ts`). Optional so older hosts
|
|
27
|
+
* without it still load the plugin.
|
|
28
|
+
*/
|
|
29
|
+
on?<K extends TypedHookName>(hookName: K, handler: TypedHookHandler<K>, opts?: TypedHookRegistrationOpts): void;
|
|
30
|
+
}
|
|
31
|
+
export interface TypedHookRegistrationOpts {
|
|
32
|
+
priority?: number;
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
}
|
|
35
|
+
/** Typed hooks this plugin registers through `api.on`. */
|
|
36
|
+
export type TypedHookName = "before_tool_call" | "after_tool_call";
|
|
37
|
+
export type TypedHookHandler<K extends TypedHookName> = K extends "before_tool_call" ? (event: ToolCallEvent, ctx?: ToolHookContext) => Promise<HookResult | void> : K extends "after_tool_call" ? (event: AfterToolCallEvent, ctx?: ToolHookContext) => Promise<void> : never;
|
|
38
|
+
/**
|
|
39
|
+
* Mirrors OpenClaw's `PluginHookBeforeToolCallEvent`. Bundle MCP tools are
|
|
40
|
+
* named `<safeServer>__<tool>` (`src/agents/agent-bundle-mcp-names.ts`).
|
|
41
|
+
*/
|
|
42
|
+
export interface ToolCallEvent {
|
|
43
|
+
toolName: string;
|
|
44
|
+
params: Record<string, unknown>;
|
|
45
|
+
/** `code_mode_exec` for code-mode tool envelopes. */
|
|
46
|
+
toolKind?: string;
|
|
47
|
+
toolInputKind?: string;
|
|
48
|
+
runId?: string;
|
|
49
|
+
toolCallId?: string;
|
|
50
|
+
}
|
|
51
|
+
/** Mirrors OpenClaw's `PluginHookAfterToolCallEvent`. */
|
|
52
|
+
export interface AfterToolCallEvent {
|
|
53
|
+
toolName: string;
|
|
54
|
+
params: Record<string, unknown>;
|
|
55
|
+
runId?: string;
|
|
56
|
+
toolCallId?: string;
|
|
57
|
+
result?: unknown;
|
|
58
|
+
error?: string;
|
|
59
|
+
durationMs?: number;
|
|
60
|
+
}
|
|
61
|
+
/** Mirrors OpenClaw's `PluginHookToolContext`. */
|
|
62
|
+
export interface ToolHookContext {
|
|
63
|
+
agentId?: string;
|
|
64
|
+
sessionKey?: string;
|
|
65
|
+
sessionId?: string;
|
|
66
|
+
runId?: string;
|
|
67
|
+
toolName?: string;
|
|
68
|
+
toolKind?: string;
|
|
69
|
+
toolCallId?: string;
|
|
22
70
|
}
|
|
23
71
|
export interface HookRegistrationOpts {
|
|
24
72
|
name?: string;
|
|
@@ -26,6 +74,7 @@ export interface HookRegistrationOpts {
|
|
|
26
74
|
}
|
|
27
75
|
export type HookEvent = "session_start" | "session_end" | "before_tool_call" | "after_tool_call" | "before_agent_start" | "before_agent_run" | "subagent_spawning" | "subagent_spawned" | "subagent_ended";
|
|
28
76
|
export type HookHandler<E extends HookEvent> = E extends "before_tool_call" ? (ctx: BeforeToolCallContext) => Promise<HookResult | void> : E extends "after_tool_call" ? (ctx: AfterToolCallContext) => Promise<void> : E extends "session_start" ? (ctx: SessionStartContext) => Promise<void> : E extends "before_agent_start" ? (ctx: BeforeAgentStartContext) => Promise<void> : E extends "before_agent_run" ? (ctx: BeforeAgentRunContext) => Promise<BeforeAgentRunResult | void> : E extends "session_end" ? (ctx: SessionEndContext) => Promise<void> : E extends "subagent_spawning" ? (ctx: SubagentSpawningContext) => Promise<SubagentSpawningResult | void> : E extends "subagent_spawned" ? (ctx: SubagentSpawnedContext) => Promise<void> : E extends "subagent_ended" ? (ctx: SubagentEndedContext) => Promise<void> : never;
|
|
77
|
+
/** Legacy `registerHook("before_tool_call")` context, for hosts without `api.on`. */
|
|
29
78
|
export interface BeforeToolCallContext {
|
|
30
79
|
toolId: string;
|
|
31
80
|
toolName: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/openclaw",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Ory plugin for OpenClaw: scaffolding skills, a local Ory instance, and authentication, authorization, and audit for every tool call",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://ory.com",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
],
|
|
69
69
|
"dependencies": {
|
|
70
70
|
"reova": "^0.7.0",
|
|
71
|
-
"@ory/argus": "1.
|
|
71
|
+
"@ory/argus": "1.6.0"
|
|
72
72
|
},
|
|
73
73
|
"engines": {
|
|
74
74
|
"node": ">=22"
|