@ory/mcp-server 0.14.0 → 1.0.1
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 +5 -5
- package/dist/tools/api.d.ts +2 -0
- package/dist/tools/api.js +89 -4
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Every Ory agent plugin in this repo registers it for you, but it's a standalone
|
|
|
9
9
|
The server speaks MCP over stdio and registers ~50 tools across two execution paths:
|
|
10
10
|
|
|
11
11
|
- **CLI-backed tools** shell out to the `ory` binary on your `PATH` (`--format json`). They cover identities, OAuth2 clients, relationships / permissions, projects, configuration, workspaces, organizations, JWKs, and event streams.
|
|
12
|
-
- **`ory_api_request`** calls the Ory Network REST API directly over HTTP. Use it for any endpoint the dedicated tools don't cover (self-service flows, sessions, recovery codes, consent challenges, schemas). The CLI
|
|
12
|
+
- **`ory_api_request`** calls the Ory Network REST API directly over HTTP. Use it for any endpoint the dedicated tools don't cover (self-service flows, sessions, recovery codes, consent challenges, schemas). The base URL comes from the `ory` CLI's selected project unless you pin one, so this tool needs no configuration of its own; an API key is only required for endpoints that demand an admin credential.
|
|
13
13
|
|
|
14
14
|
All logging goes to stderr; stdout is reserved for the MCP protocol.
|
|
15
15
|
|
|
@@ -17,7 +17,7 @@ All logging goes to stderr; stdout is reserved for the MCP protocol.
|
|
|
17
17
|
|
|
18
18
|
- **Node.js ≥ 22.**
|
|
19
19
|
- **The `ory` CLI on `PATH`** for the CLI-backed tools. Install from [ory.com/docs/guides/cli/installation](https://www.ory.com/docs/guides/cli/installation), then authenticate with `ory auth`. (Set `ORY_CLI_PATH` if the binary lives elsewhere.)
|
|
20
|
-
-
|
|
20
|
+
- **Nothing extra** for the `ory_api_request` tool — it falls back to the CLI's selected project for the base URL. Set `ORY_PROJECT_URL` to pin one, and `ORY_API_KEY` only for admin endpoints.
|
|
21
21
|
|
|
22
22
|
## Install
|
|
23
23
|
|
|
@@ -69,14 +69,14 @@ CLI-backed tools accept optional `project` and `workspace` parameters; defaults
|
|
|
69
69
|
| `ORY_PROJECT` | _(none)_ | Default project ID/slug for CLI tools |
|
|
70
70
|
| `ORY_WORKSPACE` | _(none)_ | Default workspace ID/name for CLI tools |
|
|
71
71
|
| `ORY_CLI_TIMEOUT` | `30000` | CLI command timeout (ms) |
|
|
72
|
-
| `ORY_PROJECT_URL` |
|
|
72
|
+
| `ORY_PROJECT_URL` | _the `ory` CLI's selected project_ | Base URL for `ory_api_request`. Unset, the tool asks the CLI which project is selected (`ory use project`) and derives `https://<slug>.projects.oryapis.com` from it. `ORY_PROJECT` pins which project is looked up. |
|
|
73
73
|
| `ORY_SDK_URL` | _(none)_ | Fallback base URL for `ory_api_request` when `ORY_PROJECT_URL` is unset |
|
|
74
|
-
| `ORY_API_KEY` | _(none)_ | Bearer token for `ory_api_request
|
|
74
|
+
| `ORY_API_KEY` | _(none)_ | Bearer token for `ory_api_request`. Only needed for endpoints requiring an admin credential — the CI/headless path, not the default. |
|
|
75
75
|
| `ORY_ACCESS_TOKEN` | _(none)_ | Fallback bearer for `ory_api_request` when `ORY_API_KEY` is unset |
|
|
76
76
|
| `ORY_HTTP_TIMEOUT` | `30000` | REST request timeout (ms) |
|
|
77
77
|
| `ORY_MCP_DEBUG` | `false` | Set to `true` for structured debug logging on stderr |
|
|
78
78
|
|
|
79
|
-
Per-call parameters on `ory_api_request` (`base_url`, `api_key`) override the corresponding env vars.
|
|
79
|
+
Per-call parameters on `ory_api_request` (`base_url`, `api_key`) override the corresponding env vars, and the env vars override the CLI fallback.
|
|
80
80
|
|
|
81
81
|
### Trusted hosts for `ory_api_request`
|
|
82
82
|
|
package/dist/tools/api.d.ts
CHANGED
package/dist/tools/api.js
CHANGED
|
@@ -1,15 +1,99 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resetProjectUrlCache = resetProjectUrlCache;
|
|
3
4
|
exports.isTrustedOryOrigin = isTrustedOryOrigin;
|
|
4
5
|
exports.executeApiCall = executeApiCall;
|
|
5
6
|
exports.registerApiTools = registerApiTools;
|
|
6
7
|
const zod_1 = require("zod");
|
|
7
8
|
const logger_js_1 = require("../logger.js");
|
|
9
|
+
const executor_js_1 = require("../executor.js");
|
|
8
10
|
const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
9
11
|
const TIMEOUT = Number(process.env.ORY_HTTP_TIMEOUT) || 30_000;
|
|
10
12
|
const MAX_RESPONSE_SIZE = 5 * 1024 * 1024; // 5 MB
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The project URL, resolved from the `ory` CLI's selected project.
|
|
15
|
+
*
|
|
16
|
+
* `ORY_PROJECT_URL` / `ORY_SDK_URL` are **security-half** values: a project only
|
|
17
|
+
* sets them to connect Ory Agent Security. `ory_api_request` is a DX-half tool,
|
|
18
|
+
* and every other tool in this server already works off nothing but the CLI's
|
|
19
|
+
* own session — so requiring a security-half value here made one DX tool fail
|
|
20
|
+
* on a DX-only install against a real project, before a credential was even
|
|
21
|
+
* considered (#216).
|
|
22
|
+
*
|
|
23
|
+
* Two calls, because the CLI splits the answer: `ory use project` yields the
|
|
24
|
+
* selected project's **id**, and the SDK URL is **slug**-based. A project id is
|
|
25
|
+
* a UUID and is never substituted into the URL — that would produce a hostname
|
|
26
|
+
* that does not resolve.
|
|
27
|
+
*
|
|
28
|
+
* Everything here is best-effort. A missing CLI, an expired session, no
|
|
29
|
+
* selected project, or output that does not parse all collapse to `null`, and
|
|
30
|
+
* the caller reports the same "no base URL" error it always did.
|
|
31
|
+
*/
|
|
32
|
+
async function resolveProjectUrlFromCli() {
|
|
33
|
+
const projectId = process.env.ORY_PROJECT?.trim() || (await selectedProjectId());
|
|
34
|
+
if (!projectId)
|
|
35
|
+
return null;
|
|
36
|
+
const project = await runOryJson(["get", "project", projectId, "--format", "json"]);
|
|
37
|
+
const slug = typeof project?.slug === "string" ? project.slug.trim() : "";
|
|
38
|
+
return slug ? `https://${slug}.projects.oryapis.com` : null;
|
|
39
|
+
}
|
|
40
|
+
async function selectedProjectId() {
|
|
41
|
+
const selected = await runOryJson(["use", "project", "--format", "json"]);
|
|
42
|
+
const id = typeof selected?.id === "string" ? selected.id.trim() : "";
|
|
43
|
+
// The CLI writes a zero UUID when nothing has been selected.
|
|
44
|
+
return id && !/^0{8}-0{4}-0{4}-0{4}-0{12}$/.test(id) ? id : null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Run an `ory` command and parse its stdout as JSON, or `null`.
|
|
48
|
+
*
|
|
49
|
+
* The exit code is not sufficient on its own: `ory use project` with no
|
|
50
|
+
* selection prints `Error: no project was specified` and still exits 0. Parsing
|
|
51
|
+
* is therefore the test — anything that is not a JSON object is "no answer".
|
|
52
|
+
*/
|
|
53
|
+
async function runOryJson(args) {
|
|
54
|
+
try {
|
|
55
|
+
const result = await (0, executor_js_1.runOryCommand)(args);
|
|
56
|
+
if (result.exitCode !== 0)
|
|
57
|
+
return null;
|
|
58
|
+
const parsed = JSON.parse(result.stdout);
|
|
59
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
60
|
+
? parsed
|
|
61
|
+
: null;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// No CLI on PATH, a timeout, or non-JSON output. All mean "cannot answer".
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolved project URL, cached for the life of the process.
|
|
70
|
+
*
|
|
71
|
+
* The lookup costs up to two subprocesses and `ory_api_request` is called
|
|
72
|
+
* repeatedly in a session, so a successful answer is cached. The in-flight
|
|
73
|
+
* promise is what is stored, so concurrent calls share one lookup.
|
|
74
|
+
*
|
|
75
|
+
* A **failure is deliberately not cached.** This server is long-lived — one
|
|
76
|
+
* stdio process per harness session — and the error it returns tells the user to
|
|
77
|
+
* run `ory use project`. Caching the failure would make that instruction false
|
|
78
|
+
* until they restarted the harness. The cost of re-trying is a couple of spawns
|
|
79
|
+
* on a path that is already returning an error, and a missing binary fails fast
|
|
80
|
+
* with ENOENT rather than waiting out the timeout.
|
|
81
|
+
*/
|
|
82
|
+
let cliProjectUrl;
|
|
83
|
+
/** Drop the cached CLI lookup. For tests. */
|
|
84
|
+
function resetProjectUrlCache() {
|
|
85
|
+
cliProjectUrl = undefined;
|
|
86
|
+
}
|
|
87
|
+
async function resolveBaseUrl(explicit) {
|
|
88
|
+
const fromEnv = explicit ?? process.env.ORY_PROJECT_URL ?? process.env.ORY_SDK_URL;
|
|
89
|
+
if (fromEnv)
|
|
90
|
+
return fromEnv;
|
|
91
|
+
const pending = (cliProjectUrl ??= resolveProjectUrlFromCli());
|
|
92
|
+
const resolved = await pending;
|
|
93
|
+
// Only a real answer sticks; see above.
|
|
94
|
+
if (resolved === null && cliProjectUrl === pending)
|
|
95
|
+
cliProjectUrl = undefined;
|
|
96
|
+
return resolved;
|
|
13
97
|
}
|
|
14
98
|
function resolveEnvApiKey() {
|
|
15
99
|
return process.env.ORY_API_KEY ?? process.env.ORY_ACCESS_TOKEN;
|
|
@@ -67,13 +151,14 @@ function buildUrl(baseUrl, path, query) {
|
|
|
67
151
|
return url.toString();
|
|
68
152
|
}
|
|
69
153
|
async function executeApiCall(params) {
|
|
70
|
-
const baseUrl = resolveBaseUrl(params.base_url);
|
|
154
|
+
const baseUrl = await resolveBaseUrl(params.base_url);
|
|
71
155
|
if (!baseUrl) {
|
|
72
156
|
return {
|
|
73
157
|
content: [
|
|
74
158
|
{
|
|
75
159
|
type: "text",
|
|
76
|
-
text: "No base URL is configured.
|
|
160
|
+
text: "No base URL is configured. Select a project with 'ory use project <id>', " +
|
|
161
|
+
"set the ORY_PROJECT_URL environment variable, or pass 'base_url'.",
|
|
77
162
|
},
|
|
78
163
|
],
|
|
79
164
|
isError: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "MCP server exposing the Ory CLI and Ory Network REST API as tools for AI agents to manage identities, OAuth2 clients, permissions, projects, and configuration",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://ory.com",
|
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
],
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
59
|
-
"reo-census": "^1.2.8",
|
|
60
59
|
"zod": "^4.4.3"
|
|
61
60
|
},
|
|
62
61
|
"engines": {
|