@ory/mastra 1.5.2 → 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 +28 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +22 -0
- package/dist/types.d.ts +9 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -36,6 +36,34 @@ import { withOry } from "@ory/mastra";
|
|
|
36
36
|
const tools = withOry({ search, sendEmail });
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
## MCP tools
|
|
40
|
+
|
|
41
|
+
Tools from a [`@mastra/mcp`](https://mastra.ai/docs/tools-mcp/mcp-overview) `MCPClient` are gated
|
|
42
|
+
as MCP tools: Agent Security checks `McpServer:<server>` and `McpTool:<server>:<tool>` and
|
|
43
|
+
resolves the reported server name to its catalog entry.
|
|
44
|
+
|
|
45
|
+
- `withOry` reads the server from the `mcpMetadata.serverName` the MCP client attaches to each
|
|
46
|
+
tool (the `servers` key), and strips the `<server>_` prefix from the tool id to check the
|
|
47
|
+
server's own tool name.
|
|
48
|
+
- `beforeToolCall` receives only the tool name, which `MCPClient.listTools()` builds as
|
|
49
|
+
`<server>_<tool>`. The hooks cannot tell an MCP tool from a native one by name alone, so pass
|
|
50
|
+
the server names to identify them; the longest matching server wins:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
const mcp = new MCPClient({ servers: { github: { url: new URL(githubUrl) } } });
|
|
54
|
+
const agent = new Agent({
|
|
55
|
+
/* … */
|
|
56
|
+
tools: { search, ...(await mcp.listTools()) },
|
|
57
|
+
hooks: oryAgentHooks({ mcpServers: ["github"] }),
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Without `mcpServers`, the hooks gate MCP tools as native tools under their `<server>_<tool>`
|
|
62
|
+
name. Name native tools so they do not start with a listed server name followed by `_`, or
|
|
63
|
+
they are checked as that server's tools. `listToolsets()` keys tools by their unprefixed
|
|
64
|
+
names, so the hooks cannot identify them; wrap those toolsets with `withOry`. Neither surface sees a server's URL or
|
|
65
|
+
launch command, so no server fingerprints are reported.
|
|
66
|
+
|
|
39
67
|
This package depends only on `@ory/argus` — not `@mastra/core`; tools and hook contexts are
|
|
40
68
|
duck-typed so any Mastra v1 version works. Credentials come from the shared
|
|
41
69
|
`~/.config/ory-agent-plugins/config.json`.
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
* - {@link withOry} — wraps each tool's `execute` directly (deny → throw), for callers who
|
|
10
10
|
* prefer per-tool wrapping or are on a Mastra version without the hooks API.
|
|
11
11
|
*
|
|
12
|
+
* MCP tools from `@mastra/mcp` are gated as MCP tools (`McpServer` / `McpTool`). `withOry`
|
|
13
|
+
* reads the server from each tool's `mcpMetadata`; the hooks see only the tool name, so they
|
|
14
|
+
* identify `<server>_<tool>` names only for the servers listed in `mcpServers`.
|
|
15
|
+
*
|
|
12
16
|
* The package has no `@mastra/core` dependency — tools/hook contexts are duck-typed.
|
|
13
17
|
*/
|
|
14
18
|
import { OryAgentClient } from "@ory/argus";
|
|
@@ -20,6 +24,14 @@ export interface OryMastraOptions {
|
|
|
20
24
|
projectUrl?: string;
|
|
21
25
|
/** Whether a denied tool is hard-blocked. Default true. */
|
|
22
26
|
canBlock?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Names of the `MCPClient` servers whose tools the agent uses (the keys of `servers`, e.g.
|
|
29
|
+
* `Object.keys(mcp.servers)` or `["github"]`). `@mastra/mcp` names each tool
|
|
30
|
+
* `<server>_<tool>`, and the hooks receive only that name, so a tool name is gated as an MCP
|
|
31
|
+
* tool only when it starts with one of these servers followed by `_`; the longest match wins.
|
|
32
|
+
* `withOry` reads the server from the tool's `mcpMetadata` and uses this only as a fallback.
|
|
33
|
+
*/
|
|
34
|
+
mcpServers?: Iterable<string>;
|
|
23
35
|
}
|
|
24
36
|
/**
|
|
25
37
|
* Build the `{ beforeToolCall, afterToolCall }` hooks object for `new Agent({ hooks })`.
|
|
@@ -32,4 +44,4 @@ export declare function oryAgentHooks(options?: OryMastraOptions): OryMastraHook
|
|
|
32
44
|
*/
|
|
33
45
|
export declare function withOry(tools: ToolSet, options?: OryMastraOptions): ToolSet;
|
|
34
46
|
export { OryAgentClient, OryDenialError } from "@ory/argus";
|
|
35
|
-
export type { OryMastraHooks, ToolSet, MastraTool, ToolHookBeforeContext, ToolHookAfterContext } from "./types.js";
|
|
47
|
+
export type { OryMastraHooks, ToolSet, MastraTool, MastraMcpMetadata, ToolHookBeforeContext, ToolHookAfterContext, } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
* - {@link withOry} — wraps each tool's `execute` directly (deny → throw), for callers who
|
|
11
11
|
* prefer per-tool wrapping or are on a Mastra version without the hooks API.
|
|
12
12
|
*
|
|
13
|
+
* MCP tools from `@mastra/mcp` are gated as MCP tools (`McpServer` / `McpTool`). `withOry`
|
|
14
|
+
* reads the server from each tool's `mcpMetadata`; the hooks see only the tool name, so they
|
|
15
|
+
* identify `<server>_<tool>` names only for the servers listed in `mcpServers`.
|
|
16
|
+
*
|
|
13
17
|
* The package has no `@mastra/core` dependency — tools/hook contexts are duck-typed.
|
|
14
18
|
*/
|
|
15
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -18,12 +22,24 @@ exports.oryAgentHooks = oryAgentHooks;
|
|
|
18
22
|
exports.withOry = withOry;
|
|
19
23
|
const argus_1 = require("@ory/argus");
|
|
20
24
|
const HARNESS = "mastra";
|
|
25
|
+
/** The MCP identity of a `@mastra/mcp` tool, from the `mcpMetadata` the MCP client attaches. */
|
|
26
|
+
function mcpToolFromMetadata(tool, toolName) {
|
|
27
|
+
const serverName = tool.mcpMetadata?.serverName;
|
|
28
|
+
if (typeof serverName !== "string" || serverName === "")
|
|
29
|
+
return undefined;
|
|
30
|
+
const prefix = `${serverName}_`;
|
|
31
|
+
return {
|
|
32
|
+
serverName,
|
|
33
|
+
toolName: toolName.startsWith(prefix) && toolName.length > prefix.length ? toolName.slice(prefix.length) : toolName,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
21
36
|
/**
|
|
22
37
|
* Build the `{ beforeToolCall, afterToolCall }` hooks object for `new Agent({ hooks })`.
|
|
23
38
|
*/
|
|
24
39
|
function oryAgentHooks(options = {}) {
|
|
25
40
|
const client = options.client ?? argus_1.OryAgentClient.fromEnv(HARNESS);
|
|
26
41
|
const canBlock = options.canBlock ?? true;
|
|
42
|
+
const mcpServers = options.mcpServers ? [...options.mcpServers] : undefined;
|
|
27
43
|
const ensureSession = (0, argus_1.createSessionStarter)(client, {
|
|
28
44
|
harness: HARNESS,
|
|
29
45
|
projectUrl: options.projectUrl,
|
|
@@ -40,11 +56,13 @@ function oryAgentHooks(options = {}) {
|
|
|
40
56
|
projectUrl: options.projectUrl,
|
|
41
57
|
}).catch(() => undefined);
|
|
42
58
|
}
|
|
59
|
+
const mcpTool = mcpServers ? (0, argus_1.parseMcpToolWithSeparator)(toolName, "_", mcpServers) : null;
|
|
43
60
|
const result = await (0, argus_1.gate)(client, {
|
|
44
61
|
harness: HARNESS,
|
|
45
62
|
toolName,
|
|
46
63
|
toolArgs: ctx.input,
|
|
47
64
|
canBlock,
|
|
65
|
+
mcpTool: mcpTool ?? undefined,
|
|
48
66
|
});
|
|
49
67
|
if (result.kind === "deny" && result.blocked) {
|
|
50
68
|
return { proceed: false, output: result.denialMessage ?? "Ory: permission denied" };
|
|
@@ -64,6 +82,7 @@ function oryAgentHooks(options = {}) {
|
|
|
64
82
|
function withOry(tools, options = {}) {
|
|
65
83
|
const client = options.client ?? argus_1.OryAgentClient.fromEnv(HARNESS);
|
|
66
84
|
const canBlock = options.canBlock ?? true;
|
|
85
|
+
const mcpServers = options.mcpServers ? [...options.mcpServers] : undefined;
|
|
67
86
|
const ensureSession = (0, argus_1.createSessionStarter)(client, {
|
|
68
87
|
harness: HARNESS,
|
|
69
88
|
projectUrl: options.projectUrl,
|
|
@@ -75,6 +94,8 @@ function withOry(tools, options = {}) {
|
|
|
75
94
|
continue;
|
|
76
95
|
}
|
|
77
96
|
const toolName = tool.id ?? key;
|
|
97
|
+
const mcpTool = mcpToolFromMetadata(tool, toolName) ??
|
|
98
|
+
(mcpServers ? (0, argus_1.parseMcpToolWithSeparator)(toolName, "_", mcpServers) ?? undefined : undefined);
|
|
78
99
|
const originalExecute = tool.execute.bind(tool);
|
|
79
100
|
wrapped[key] = {
|
|
80
101
|
...tool,
|
|
@@ -85,6 +106,7 @@ function withOry(tools, options = {}) {
|
|
|
85
106
|
toolName,
|
|
86
107
|
toolArgs: inputData,
|
|
87
108
|
canBlock,
|
|
109
|
+
mcpTool,
|
|
88
110
|
});
|
|
89
111
|
if (result.blocked) {
|
|
90
112
|
throw new argus_1.OryDenialError({
|
package/dist/types.d.ts
CHANGED
|
@@ -25,9 +25,18 @@ export interface OryMastraHooks {
|
|
|
25
25
|
beforeToolCall: (ctx: ToolHookBeforeContext) => Promise<void | ToolBeforeHookResult> | void | ToolBeforeHookResult;
|
|
26
26
|
afterToolCall: (ctx: ToolHookAfterContext) => Promise<void> | void;
|
|
27
27
|
}
|
|
28
|
+
/** Server metadata `@mastra/mcp` attaches to the tools its `MCPClient` creates. */
|
|
29
|
+
export interface MastraMcpMetadata {
|
|
30
|
+
/** The `MCPClient` `servers` key the tool came from. */
|
|
31
|
+
serverName?: string;
|
|
32
|
+
serverVersion?: string;
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
}
|
|
28
35
|
/** A Mastra tool — duck-typed. `execute` takes `(inputData, context)` in v1. */
|
|
29
36
|
export interface MastraTool {
|
|
30
37
|
id?: string;
|
|
38
|
+
/** Present on tools from a `@mastra/mcp` `MCPClient`; the tool `id` is `<server>_<tool>`. */
|
|
39
|
+
mcpMetadata?: MastraMcpMetadata;
|
|
31
40
|
execute?: (inputData: unknown, context?: unknown) => unknown;
|
|
32
41
|
[key: string]: unknown;
|
|
33
42
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/mastra",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Ory Agent Security for Mastra — per-tool authorization, activity auditing, and identity propagation via agent tool-call hooks (or wrapping tool execute). Built on @ory/argus.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"reova": "^0.7.0",
|
|
37
|
-
"@ory/argus": "1.
|
|
37
|
+
"@ory/argus": "1.6.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"typescript": "^
|
|
40
|
+
"typescript": "^7.0.2"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "tsc",
|