@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 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 grants, 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.
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
- api.registerHook("before_tool_call", createBeforeToolCallHandler(client, subagents), {
26
- name: "ory.permission-check",
27
- description: "Check tool permissions via Ory Permissions",
28
- });
29
- api.registerHook("after_tool_call", createAfterToolCallHandler(client), {
30
- name: "ory.activity-complete",
31
- description: "Record tool completion activity",
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: (0, argus_1.parseMcpToolGeneric)(ctx.toolId) ?? undefined,
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.5.3",
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.5.3"
71
+ "@ory/argus": "1.6.0"
72
72
  },
73
73
  "engines": {
74
74
  "node": ">=22"