@rekey.dev/mcp 1.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ReliPay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # `@rekey.dev/mcp`
2
+
3
+ A [Model Context Protocol](https://modelcontextprotocol.io) server for [Rekey](https://relipay.dev). Lets Claude Desktop, Cursor, and Claude Code introspect a deployment — and mint API keys — directly, without screenshots or copy-paste from the panel.
4
+
5
+ > **For AI agents connected through this server:** see [AGENTS.md](./AGENTS.md) for what's safe to call and the write-tool safety model.
6
+
7
+ ```bash
8
+ # Run on demand (no global install needed):
9
+ npx -y @rekey.dev/mcp
10
+ ```
11
+
12
+ It's a stdio MCP server — you don't run it by hand. Wire it into your MCP client's config (below) and the client launches it.
13
+
14
+ ## Setup
15
+
16
+ The server reads three env vars (set them in your MCP client config, not your shell):
17
+
18
+ | Env var | Required | What it's for | Where to get it |
19
+ | --- | --- | --- | --- |
20
+ | `RELIPAY_URL` | yes | Base URL of the deployment, e.g. `https://api.relipay.dev`. | Your Rekey deployment |
21
+ | `SUPER_ADMIN_KEY` | for **read** tools | Authenticates the global read/introspection tools. | Deployment admin |
22
+ | `RELIPAY_OPERATOR_TOKEN` | for `mint_api_key` | A scoped operator **personal-access-token** (`rp_op_…`). | Panel → operator API tokens (or `POST /api/v1/tenant/auth/api-tokens`), scoped to `keys:mint` |
23
+
24
+ `RELIPAY_URL` plus **at least one** credential is required. Set `SUPER_ADMIN_KEY` for the read tools, `RELIPAY_OPERATOR_TOKEN` for the `keys:mint` write tool, or both. An agent that should only mint keys can run with the operator token alone (no master key) — the read tools then fail closed with `READ_REQUIRES_ADMIN_KEY`. The server exits with a clear stderr message if `RELIPAY_URL` or every credential is missing.
25
+
26
+ > The `mint_api_key` write tool does **not** use the all-powerful
27
+ > `SUPER_ADMIN_KEY`. It authenticates as a **scoped operator** via
28
+ > `RELIPAY_OPERATOR_TOKEN`, which must carry the `keys:mint` scope and belong to
29
+ > the workspace that owns the target Application — default-deny, so an agent
30
+ > can't mutate production unless you explicitly grant it.
31
+
32
+ ## Quickstart
33
+
34
+ ### Claude Desktop
35
+
36
+ `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS), or the equivalent on your platform:
37
+
38
+ ```json
39
+ {
40
+ "mcpServers": {
41
+ "rekey": {
42
+ "command": "npx",
43
+ "args": ["-y", "@rekey.dev/mcp"],
44
+ "env": {
45
+ "RELIPAY_URL": "https://api.relipay.dev",
46
+ "SUPER_ADMIN_KEY": "your-admin-key",
47
+ "RELIPAY_OPERATOR_TOKEN": "rp_op_…"
48
+ }
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ Restart Claude Desktop. Tools appear as `mcp__relipay__list_applications`, etc.
55
+
56
+ ### Cursor
57
+
58
+ Settings → MCP → Add Server. Same `command` / `args` / `env` shape.
59
+
60
+ ### Claude Code
61
+
62
+ `.claude/mcp.json` in your repo (same shape). Omit `RELIPAY_OPERATOR_TOKEN` if you only need read tools, or omit `SUPER_ADMIN_KEY` if the agent only needs the `mint_api_key` write tool.
63
+
64
+ ## Tools
65
+
66
+ **Read tools** (authenticated with `SUPER_ADMIN_KEY`):
67
+
68
+ | Tool | What it does |
69
+ | --- | --- |
70
+ | `list_tenants` | All Tenants in the deployment. |
71
+ | `list_applications` | Applications, optionally filtered by `tenantId`. |
72
+ | `get_application` | One Application by id (with authConfig + billingConfig — no secrets). |
73
+ | `list_plans` | Plans for an Application; `includeInactive` for archived ones. |
74
+ | `list_coupons` | Coupons for an Application; `includeInactive` for deactivated ones. |
75
+ | `list_api_keys` | Active API keys for an Application — metadata only; the hash is never returned. |
76
+ | `list_payments` | Recent payments (newest first), filterable by `applicationId` / `status` (`PENDING`/`SUCCEEDED`/`FAILED`/`REFUNDED`), exact-match `q` (payment id / provider payment id / end-user id), `sort` (`createdAt`/`amount`), `order`, `limit` (≤200). Amounts are integers in the smallest currency unit. |
77
+ | `get_payment_stats_by_app` | Per-application payment health over the last 30 days — succeeded/failed/pending/refunded counts, success rate, SUCCEEDED volume (`volumeCents`). The panel's richer per-app Billing Overview (`MRR`, 12-month series) lives at the **panel-session-only** endpoint `/api/v1/tenant/applications/:id/billing/stats` and is not reachable with this server's credentials. |
78
+
79
+ **Write tools** (authenticated with the scoped `RELIPAY_OPERATOR_TOKEN`):
80
+
81
+ | Tool | What it does |
82
+ | --- | --- |
83
+ | `mint_api_key` | Mint a new secret key (`rp_live_…` / `rp_test_…`) for an Application. Args: `applicationId`, `name`, `mode?` (`live`/`test`), `scopes?`. The raw key is returned **exactly once** — surface it immediately and warn it's unrecoverable. |
84
+
85
+ ### `mint_api_key` failure modes (default-deny)
86
+
87
+ | Condition | Error |
88
+ | --- | --- |
89
+ | No `RELIPAY_OPERATOR_TOKEN` configured | `OPERATOR_TOKEN_MISSING` — never falls back to the admin key. |
90
+ | PAT lacks the `keys:mint` scope | `OPERATOR_SCOPE_INSUFFICIENT` (403). |
91
+ | PAT bound to a different workspace than the Application | `APPLICATION_NOT_FOUND` (404) — no cross-tenant oracle. |
92
+ | PAT revoked / expired | `OPERATOR_TOKEN_INVALID` (401). |
93
+
94
+ ## Errors
95
+
96
+ Every tool error is a JSON object — `error: { code, message, fix?, statusCode? }` — in the response content (with `isError: true`). Read `fix` first. Common codes: `TOOL_NOT_FOUND`, `TOOL_ARGS_INVALID`, `APPLICATION_NOT_FOUND`, `ADMIN_AUTH_INVALID`.
97
+
98
+ ## Gotchas
99
+
100
+ - **Money is integers in the smallest currency unit** (cents/paise/sen). Render `999 USD/MONTH` as `$9.99/month` — never assume a `.00` decimal (`¥100` is a hundred whole yen).
101
+ - **Only call `mint_api_key` when the user explicitly asks** to create/mint a key. For other mutations (create plan, refund, deactivate coupon — not yet exposed), use the read tools to figure out what to do, then the `rekey` CLI or the Panel.
102
+ - **Coupon discounts**: PERCENT uses basis-points × 10 (`1500` = 15%); AMOUNT uses the smallest currency unit.
103
+
104
+ ## Links
105
+
106
+ - Docs: [/docs](https://relipay.dev/docs) · [SDK guide](https://relipay.dev/docs/sdk) · [API reference](https://relipay.dev/docs/api) · [agent prompt](https://relipay.dev/docs/prompt)
107
+
108
+ ## License
109
+
110
+ MIT
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Admin-API client used by every MCP tool. Mirrors `packages/cli/src/lib/api.ts`
3
+ * but is decoupled (the MCP server doesn't depend on the CLI).
4
+ */
5
+ export interface AdminClientConfig {
6
+ apiUrl: string;
7
+ /**
8
+ * Global super-admin key. Authenticates the (global-scope) READ tools.
9
+ * Optional: when unset, the server can still run for the scoped write tool —
10
+ * read tools then fail closed with `READ_REQUIRES_ADMIN_KEY` rather than
11
+ * silently using a lesser credential.
12
+ */
13
+ adminKey?: string;
14
+ /**
15
+ * Operator personal-access-token (`rp_op_…`), if configured. Used ONLY by
16
+ * write tools, which authenticate as a scoped operator rather than with the
17
+ * all-powerful `adminKey`. Read tools never touch this. Optional: when unset,
18
+ * write tools refuse to run with a clear `OPERATOR_TOKEN_MISSING` error.
19
+ */
20
+ operatorToken?: string;
21
+ }
22
+ export declare class AdminApiError extends Error {
23
+ readonly code: string;
24
+ readonly fix: string | undefined;
25
+ readonly statusCode: number;
26
+ constructor(args: {
27
+ code: string;
28
+ message: string;
29
+ fix?: string;
30
+ statusCode: number;
31
+ });
32
+ }
33
+ export declare class AdminClient {
34
+ private readonly cfg;
35
+ constructor(cfg: AdminClientConfig);
36
+ /** Read-path request — authenticated with the admin key. */
37
+ request<T>(method: string, path: string, body?: unknown): Promise<T>;
38
+ /**
39
+ * Write-path request — authenticated with the configured operator PAT
40
+ * (`rp_op_…`). Used by write tools so an agent acts as a SCOPED operator, not
41
+ * as the global super-admin. Throws `OPERATOR_TOKEN_MISSING` if no PAT was
42
+ * configured — write tools must fail closed, never silently fall back to the
43
+ * admin key.
44
+ */
45
+ requestAsOperator<T>(method: string, path: string, body?: unknown): Promise<T>;
46
+ private send;
47
+ }
48
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,SAAgB,UAAU,EAAE,MAAM,CAAC;gBACvB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;CAOtF;AAED,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,iBAAiB;IAInD,4DAA4D;IACtD,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAY1E;;;;;;OAMG;IACG,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;YAYtE,IAAI;CA0BnB"}
package/dist/client.js ADDED
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Admin-API client used by every MCP tool. Mirrors `packages/cli/src/lib/api.ts`
3
+ * but is decoupled (the MCP server doesn't depend on the CLI).
4
+ */
5
+ export class AdminApiError extends Error {
6
+ code;
7
+ fix;
8
+ statusCode;
9
+ constructor(args) {
10
+ super(args.message);
11
+ this.name = 'AdminApiError';
12
+ this.code = args.code;
13
+ this.fix = args.fix;
14
+ this.statusCode = args.statusCode;
15
+ }
16
+ }
17
+ export class AdminClient {
18
+ cfg;
19
+ constructor(cfg) {
20
+ this.cfg = cfg;
21
+ if (!cfg.apiUrl)
22
+ throw new Error('AdminClient: apiUrl is required.');
23
+ }
24
+ /** Read-path request — authenticated with the admin key. */
25
+ async request(method, path, body) {
26
+ if (!this.cfg.adminKey) {
27
+ throw new AdminApiError({
28
+ code: 'READ_REQUIRES_ADMIN_KEY',
29
+ message: 'This read/introspection tool requires the global SUPER_ADMIN_KEY.',
30
+ fix: 'Set SUPER_ADMIN_KEY in the MCP server env to enable read tools. (The keys:mint write tool only needs RELIPAY_OPERATOR_TOKEN.)',
31
+ statusCode: 400,
32
+ });
33
+ }
34
+ return this.send(method, path, this.cfg.adminKey, body);
35
+ }
36
+ /**
37
+ * Write-path request — authenticated with the configured operator PAT
38
+ * (`rp_op_…`). Used by write tools so an agent acts as a SCOPED operator, not
39
+ * as the global super-admin. Throws `OPERATOR_TOKEN_MISSING` if no PAT was
40
+ * configured — write tools must fail closed, never silently fall back to the
41
+ * admin key.
42
+ */
43
+ async requestAsOperator(method, path, body) {
44
+ if (!this.cfg.operatorToken) {
45
+ throw new AdminApiError({
46
+ code: 'OPERATOR_TOKEN_MISSING',
47
+ message: 'This write tool requires an operator personal-access-token.',
48
+ fix: 'Set RELIPAY_OPERATOR_TOKEN (an rp_op_… token minted in the Panel, scoped to keys:mint) in the MCP server env.',
49
+ statusCode: 400,
50
+ });
51
+ }
52
+ return this.send(method, path, this.cfg.operatorToken, body);
53
+ }
54
+ async send(method, path, bearer, body) {
55
+ const res = await fetch(`${this.cfg.apiUrl.replace(/\/$/, '')}${path}`, {
56
+ method,
57
+ headers: {
58
+ Authorization: `Bearer ${bearer}`,
59
+ ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
60
+ },
61
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
62
+ });
63
+ const json = (await res.json().catch(() => ({})));
64
+ if (!res.ok || ('success' in json && json.success === false)) {
65
+ const err = 'error' in json
66
+ ? json.error
67
+ : { code: 'UNKNOWN_ERROR', message: `HTTP ${res.status}` };
68
+ throw new AdminApiError({ ...err, statusCode: res.status });
69
+ }
70
+ return json.data;
71
+ }
72
+ }
73
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @rekey.dev/mcp — Model Context Protocol server.
4
+ *
5
+ * Speaks MCP over stdio, exposing introspection tools for Rekey
6
+ * (Applications, Plans, Coupons, API keys, Tenants) plus a single guarded
7
+ * WRITE tool (`mint_api_key`). Designed to be wired into Claude Desktop,
8
+ * Cursor, and Claude Code so an AI agent can answer "what plans does this app
9
+ * have?" or mint a fresh API key without screenshots.
10
+ *
11
+ * Read tools use `SUPER_ADMIN_KEY` (global scope). The write tool uses a SCOPED
12
+ * operator personal-access-token (`RELIPAY_OPERATOR_TOKEN`, an `rp_op_…` token)
13
+ * and is rejected server-side unless that PAT carries the `keys:mint` scope —
14
+ * default-deny, so an agent can't mutate production unless explicitly granted.
15
+ *
16
+ * At least ONE credential is required. An agent that should only mint keys can
17
+ * run with `RELIPAY_OPERATOR_TOKEN` alone (no master key) — read tools then fail
18
+ * closed with `READ_REQUIRES_ADMIN_KEY`. Configure `SUPER_ADMIN_KEY` too to
19
+ * enable the global read/introspection tools.
20
+ */
21
+ export {};
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;GAkBG"}
package/dist/index.js ADDED
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @rekey.dev/mcp — Model Context Protocol server.
4
+ *
5
+ * Speaks MCP over stdio, exposing introspection tools for Rekey
6
+ * (Applications, Plans, Coupons, API keys, Tenants) plus a single guarded
7
+ * WRITE tool (`mint_api_key`). Designed to be wired into Claude Desktop,
8
+ * Cursor, and Claude Code so an AI agent can answer "what plans does this app
9
+ * have?" or mint a fresh API key without screenshots.
10
+ *
11
+ * Read tools use `SUPER_ADMIN_KEY` (global scope). The write tool uses a SCOPED
12
+ * operator personal-access-token (`RELIPAY_OPERATOR_TOKEN`, an `rp_op_…` token)
13
+ * and is rejected server-side unless that PAT carries the `keys:mint` scope —
14
+ * default-deny, so an agent can't mutate production unless explicitly granted.
15
+ *
16
+ * At least ONE credential is required. An agent that should only mint keys can
17
+ * run with `RELIPAY_OPERATOR_TOKEN` alone (no master key) — read tools then fail
18
+ * closed with `READ_REQUIRES_ADMIN_KEY`. Configure `SUPER_ADMIN_KEY` too to
19
+ * enable the global read/introspection tools.
20
+ */
21
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
22
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
23
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
24
+ import { zodToJsonSchema } from './lib/zod-to-json-schema.js';
25
+ import { AdminClient, AdminApiError } from './client.js';
26
+ import { tools } from './tools.js';
27
+ const apiUrl = process.env.RELIPAY_URL ?? '';
28
+ const adminKey = process.env.SUPER_ADMIN_KEY ?? '';
29
+ // Optional: only required by the `mint_api_key` write tool. Read tools work
30
+ // without it. When absent, the write tool fails closed with a clear error.
31
+ const operatorToken = process.env.RELIPAY_OPERATOR_TOKEN ?? '';
32
+ if (!apiUrl || (!adminKey && !operatorToken)) {
33
+ process.stderr.write(`[rekey-mcp] missing required env: RELIPAY_URL plus at least one credential ` +
34
+ `(SUPER_ADMIN_KEY for read tools and/or RELIPAY_OPERATOR_TOKEN for the keys:mint write tool).\n` +
35
+ `[rekey-mcp] fix: configure these in the MCP client (Claude Desktop config, Cursor MCP settings, …)\n`);
36
+ process.exit(1);
37
+ }
38
+ const client = new AdminClient({
39
+ apiUrl,
40
+ ...(adminKey ? { adminKey } : {}),
41
+ ...(operatorToken ? { operatorToken } : {}),
42
+ });
43
+ const server = new Server({ name: 'rekey-mcp', version: '0.0.0' }, { capabilities: { tools: {} } });
44
+ server.setRequestHandler(ListToolsRequestSchema, () => ({
45
+ tools: tools.map((t) => ({
46
+ name: t.name,
47
+ description: t.description,
48
+ inputSchema: zodToJsonSchema(t.inputSchema),
49
+ })),
50
+ }));
51
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
52
+ const tool = tools.find((t) => t.name === req.params.name);
53
+ if (!tool) {
54
+ return {
55
+ content: [
56
+ {
57
+ type: 'text',
58
+ text: JSON.stringify({
59
+ error: {
60
+ code: 'TOOL_NOT_FOUND',
61
+ message: `Unknown tool: ${req.params.name}`,
62
+ fix: 'Call list_tools to see what is available.',
63
+ },
64
+ }, null, 2),
65
+ },
66
+ ],
67
+ isError: true,
68
+ };
69
+ }
70
+ const parsed = tool.inputSchema.safeParse(req.params.arguments ?? {});
71
+ if (!parsed.success) {
72
+ return {
73
+ content: [
74
+ {
75
+ type: 'text',
76
+ text: JSON.stringify({
77
+ error: {
78
+ code: 'TOOL_ARGS_INVALID',
79
+ message: 'Tool arguments failed validation.',
80
+ fix: 'Re-call with arguments matching inputSchema. Issues: ' +
81
+ parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; '),
82
+ },
83
+ }, null, 2),
84
+ },
85
+ ],
86
+ isError: true,
87
+ };
88
+ }
89
+ try {
90
+ const result = await tool.execute(client, parsed.data);
91
+ return {
92
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
93
+ };
94
+ }
95
+ catch (err) {
96
+ if (err instanceof AdminApiError) {
97
+ return {
98
+ content: [
99
+ {
100
+ type: 'text',
101
+ text: JSON.stringify({
102
+ error: { code: err.code, message: err.message, fix: err.fix, statusCode: err.statusCode },
103
+ }, null, 2),
104
+ },
105
+ ],
106
+ isError: true,
107
+ };
108
+ }
109
+ return {
110
+ content: [
111
+ {
112
+ type: 'text',
113
+ text: JSON.stringify({
114
+ error: {
115
+ code: 'TOOL_EXECUTION_FAILED',
116
+ message: err instanceof Error ? err.message : String(err),
117
+ },
118
+ }, null, 2),
119
+ },
120
+ ],
121
+ isError: true,
122
+ };
123
+ }
124
+ });
125
+ const transport = new StdioServerTransport();
126
+ void server.connect(transport);
127
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Tiny Zod → JSON Schema shim sufficient for MCP tool input descriptors.
3
+ *
4
+ * The MCP `tools/list` response wants each tool's `inputSchema` as a JSON
5
+ * Schema object. We only use a small Zod subset (objects of strings /
6
+ * booleans / optionals with descriptions), so reaching for `zod-to-json-schema`
7
+ * as a full dependency is overkill — this 30-line implementation covers it
8
+ * and stays auditable. If the tool surface grows, swap in the npm package.
9
+ */
10
+ import { z } from 'zod';
11
+ interface JsonSchema {
12
+ type: string;
13
+ properties?: Record<string, JsonSchema>;
14
+ required?: string[];
15
+ description?: string;
16
+ items?: JsonSchema;
17
+ enum?: string[];
18
+ }
19
+ export declare function zodToJsonSchema(schema: z.ZodTypeAny): JsonSchema;
20
+ export {};
21
+ //# sourceMappingURL=zod-to-json-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-to-json-schema.d.ts","sourceRoot":"","sources":["../../src/lib/zod-to-json-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,GAAG,UAAU,CA2BhE"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Tiny Zod → JSON Schema shim sufficient for MCP tool input descriptors.
3
+ *
4
+ * The MCP `tools/list` response wants each tool's `inputSchema` as a JSON
5
+ * Schema object. We only use a small Zod subset (objects of strings /
6
+ * booleans / optionals with descriptions), so reaching for `zod-to-json-schema`
7
+ * as a full dependency is overkill — this 30-line implementation covers it
8
+ * and stays auditable. If the tool surface grows, swap in the npm package.
9
+ */
10
+ import { z } from 'zod';
11
+ export function zodToJsonSchema(schema) {
12
+ // Object — the common top-level case for tool args.
13
+ if (schema instanceof z.ZodObject) {
14
+ const shape = schema.shape;
15
+ const properties = {};
16
+ const required = [];
17
+ for (const [key, value] of Object.entries(shape)) {
18
+ const inner = unwrap(value);
19
+ properties[key] = zodToJsonSchema(inner.schema);
20
+ if (inner.description)
21
+ properties[key].description = inner.description;
22
+ if (!inner.optional)
23
+ required.push(key);
24
+ }
25
+ return {
26
+ type: 'object',
27
+ properties,
28
+ ...(required.length > 0 && { required }),
29
+ };
30
+ }
31
+ if (schema instanceof z.ZodString)
32
+ return { type: 'string' };
33
+ if (schema instanceof z.ZodEnum)
34
+ return { type: 'string', enum: [...schema.options] };
35
+ if (schema instanceof z.ZodNumber)
36
+ return { type: 'number' };
37
+ if (schema instanceof z.ZodBoolean)
38
+ return { type: 'boolean' };
39
+ if (schema instanceof z.ZodArray) {
40
+ return { type: 'array', items: zodToJsonSchema(schema.element) };
41
+ }
42
+ // Fallback — let MCP receive an open object.
43
+ return { type: 'object' };
44
+ }
45
+ function unwrap(s) {
46
+ let optional = false;
47
+ let description;
48
+ let cur = s;
49
+ // Unwrap z.optional() and pick up .describe() metadata.
50
+ while (true) {
51
+ description = description ?? cur.description;
52
+ if (cur instanceof z.ZodOptional) {
53
+ optional = true;
54
+ cur = cur.unwrap();
55
+ }
56
+ else {
57
+ break;
58
+ }
59
+ }
60
+ return { schema: cur, optional, description };
61
+ }
62
+ //# sourceMappingURL=zod-to-json-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-to-json-schema.js","sourceRoot":"","sources":["../../src/lib/zod-to-json-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,MAAM,UAAU,eAAe,CAAC,MAAoB;IAClD,oDAAoD;IACpD,IAAI,MAAM,YAAY,CAAC,CAAC,SAAS,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAqC,CAAC;QAC3D,MAAM,UAAU,GAA+B,EAAE,CAAC;QAClD,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5B,UAAU,CAAC,GAAG,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAChD,IAAI,KAAK,CAAC,WAAW;gBAAE,UAAU,CAAC,GAAG,CAAC,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;YACvE,IAAI,CAAC,KAAK,CAAC,QAAQ;gBAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,UAAU;YACV,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;SACzC,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,YAAY,CAAC,CAAC,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC7D,IAAI,MAAM,YAAY,CAAC,CAAC,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAI,MAAM,CAAC,OAAoB,CAAC,EAAE,CAAC;IACpG,IAAI,MAAM,YAAY,CAAC,CAAC,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC7D,IAAI,MAAM,YAAY,CAAC,CAAC,UAAU;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC/D,IAAI,MAAM,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;QACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;IACnE,CAAC;IACD,6CAA6C;IAC7C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED,SAAS,MAAM,CAAC,CAAe;IAK7B,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,WAA+B,CAAC;IACpC,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,wDAAwD;IACxD,OAAO,IAAI,EAAE,CAAC;QACZ,WAAW,GAAG,WAAW,IAAI,GAAG,CAAC,WAAW,CAAC;QAC7C,IAAI,GAAG,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;YACjC,QAAQ,GAAG,IAAI,CAAC;YAChB,GAAG,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,MAAM;QACR,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;AAChD,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * MCP tool registry. Each tool is a `(client, args) → result` function with
3
+ * a Zod-described input schema. The server (index.ts) wires them up.
4
+ *
5
+ * Read tools authenticate with the admin key. The one WRITE tool
6
+ * (`mint_api_key`) authenticates instead with a SCOPED operator
7
+ * personal-access-token (`RELIPAY_OPERATOR_TOKEN`) and is rejected server-side
8
+ * unless that PAT carries the `keys:mint` scope — default-deny, so an agent
9
+ * can't mint production keys unless explicitly granted. See AGENTS.md.
10
+ */
11
+ import { z } from 'zod';
12
+ import type { AdminClient } from './client.js';
13
+ export interface ToolDefinition<TArgs extends z.ZodRawShape> {
14
+ name: string;
15
+ description: string;
16
+ inputSchema: z.ZodObject<TArgs>;
17
+ execute: (client: AdminClient, args: z.infer<z.ZodObject<TArgs>>) => Promise<unknown>;
18
+ }
19
+ export declare const tools: Array<ToolDefinition<z.ZodRawShape>>;
20
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,WAAW,cAAc,CAAC,KAAK,SAAS,CAAC,CAAC,WAAW;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAChC,OAAO,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACvF;AA4DD,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,WAAW,CAAC,CAuHtD,CAAC"}
package/dist/tools.js ADDED
@@ -0,0 +1,153 @@
1
+ /**
2
+ * MCP tool registry. Each tool is a `(client, args) → result` function with
3
+ * a Zod-described input schema. The server (index.ts) wires them up.
4
+ *
5
+ * Read tools authenticate with the admin key. The one WRITE tool
6
+ * (`mint_api_key`) authenticates instead with a SCOPED operator
7
+ * personal-access-token (`RELIPAY_OPERATOR_TOKEN`) and is rejected server-side
8
+ * unless that PAT carries the `keys:mint` scope — default-deny, so an agent
9
+ * can't mint production keys unless explicitly granted. See AGENTS.md.
10
+ */
11
+ import { z } from 'zod';
12
+ const ListApplicationsArgs = z.object({
13
+ tenantId: z.string().optional().describe('Optional Tenant id to filter by.'),
14
+ });
15
+ const GetApplicationArgs = z.object({
16
+ applicationId: z.string().describe('Application id (cuid).'),
17
+ });
18
+ const ListPlansArgs = z.object({
19
+ applicationId: z.string().describe('Application id to list plans for.'),
20
+ includeInactive: z.boolean().optional().describe('If true, include deactivated plans.'),
21
+ });
22
+ const ListCouponsArgs = z.object({
23
+ applicationId: z.string().describe('Application id to list coupons for.'),
24
+ includeInactive: z.boolean().optional(),
25
+ });
26
+ const ListApiKeysArgs = z.object({
27
+ applicationId: z.string().describe('Application id to list active API keys for.'),
28
+ });
29
+ const MintApiKeyArgs = z.object({
30
+ applicationId: z
31
+ .string()
32
+ .describe('Application id to mint a secret key for. Must belong to the operator PAT\'s workspace.'),
33
+ name: z
34
+ .string()
35
+ .describe('Human label for the key, shown in lists (e.g. "ci-deploy", "agent-worker").'),
36
+ mode: z
37
+ .enum(['live', 'test'])
38
+ .optional()
39
+ .describe('"live" (default) or "test". Test keys hit sandbox billing providers.'),
40
+ scopes: z
41
+ .array(z.string())
42
+ .optional()
43
+ .describe('Application-key scopes (e.g. ["auth:read"]). Omit for the default ["*"].'),
44
+ });
45
+ const ListTenantsArgs = z.object({});
46
+ const ListPaymentsArgs = z.object({
47
+ applicationId: z.string().optional().describe('Filter to one Application id.'),
48
+ status: z
49
+ .enum(['PENDING', 'SUCCEEDED', 'FAILED', 'REFUNDED'])
50
+ .optional()
51
+ .describe('Filter by payment status.'),
52
+ q: z
53
+ .string()
54
+ .optional()
55
+ .describe('Exact-match search: payment id, provider payment id, or end-user id.'),
56
+ sort: z.enum(['createdAt', 'amount']).optional().describe('Sort field. Default createdAt.'),
57
+ order: z.enum(['asc', 'desc']).optional().describe('Sort direction. Default desc.'),
58
+ limit: z.number().int().min(1).max(200).optional().describe('Page size, 1–200. Default 50.'),
59
+ });
60
+ const PaymentStatsByAppArgs = z.object({});
61
+ export const tools = [
62
+ {
63
+ name: 'list_tenants',
64
+ description: 'List all Tenants. Tenants are the outer multi-tenancy unit (one per Rekey customer). Use this when the user asks "what tenants exist" or to find a tenantId to drill into.',
65
+ inputSchema: ListTenantsArgs,
66
+ execute: (c) => c.request('GET', '/api/v1/admin/tenants'),
67
+ },
68
+ {
69
+ name: 'list_applications',
70
+ description: 'List Applications, optionally filtered by Tenant. An Application is one project under a Tenant; every domain row in Rekey carries an applicationId. Use this to find the applicationId for subsequent calls.',
71
+ inputSchema: ListApplicationsArgs,
72
+ execute: (c, args) => {
73
+ const path = args.tenantId
74
+ ? `/api/v1/admin/applications?tenantId=${encodeURIComponent(args.tenantId)}`
75
+ : '/api/v1/admin/applications';
76
+ return c.request('GET', path);
77
+ },
78
+ },
79
+ {
80
+ name: 'get_application',
81
+ description: 'Fetch one Application by id, including its authConfig and billingConfig (no secrets).',
82
+ inputSchema: GetApplicationArgs,
83
+ execute: (c, args) => c.request('GET', `/api/v1/admin/applications/${encodeURIComponent(args.applicationId)}`),
84
+ },
85
+ {
86
+ name: 'list_plans',
87
+ description: 'List Plans for an Application. Amount is always in the smallest currency unit (cents/paise/sen) — never floats.',
88
+ inputSchema: ListPlansArgs,
89
+ execute: (c, args) => c.request('GET', `/api/v1/admin/applications/${encodeURIComponent(args.applicationId)}/plans${args.includeInactive ? '?includeInactive=true' : ''}`),
90
+ },
91
+ {
92
+ name: 'list_coupons',
93
+ description: 'List discount coupons for an Application. PERCENT discounts use basis-points × 10 (1500 = 15%); AMOUNT discounts use the smallest currency unit.',
94
+ inputSchema: ListCouponsArgs,
95
+ execute: (c, args) => c.request('GET', `/api/v1/admin/applications/${encodeURIComponent(args.applicationId)}/coupons${args.includeInactive ? '?includeInactive=true' : ''}`),
96
+ },
97
+ {
98
+ name: 'list_api_keys',
99
+ description: 'List active API keys for an Application. Returns metadata only — the raw key value is unrecoverable after creation, by design.',
100
+ inputSchema: ListApiKeysArgs,
101
+ execute: (c, args) => c.request('GET', `/api/v1/admin/applications/${encodeURIComponent(args.applicationId)}/api-keys`),
102
+ },
103
+ {
104
+ name: 'list_payments',
105
+ description: 'List recent payments (newest first), optionally filtered to one Application and/or status. ' +
106
+ 'Each row carries applicationId, applicationSlug, endUserId, amount, currency, status, createdAt. ' +
107
+ 'Amounts are integers in the smallest currency unit (cents/paise/sen) — never floats.',
108
+ inputSchema: ListPaymentsArgs,
109
+ execute: async (c, args) => {
110
+ const p = new URLSearchParams();
111
+ if (args.applicationId !== undefined)
112
+ p.set('applicationId', args.applicationId);
113
+ if (args.status !== undefined)
114
+ p.set('status', args.status);
115
+ if (args.q !== undefined)
116
+ p.set('q', args.q);
117
+ if (args.sort !== undefined)
118
+ p.set('sort', args.sort);
119
+ if (args.order !== undefined)
120
+ p.set('order', args.order);
121
+ if (args.limit !== undefined)
122
+ p.set('limit', String(args.limit));
123
+ const qs = p.toString();
124
+ // /admin/metrics/payments returns a paginated Page<T> envelope
125
+ // ({ items, total, limit, offset }). This tool's contract is a bare
126
+ // payments array (bounded by `limit`), so unwrap to `.items`.
127
+ const page = await c.request('GET', `/api/v1/admin/metrics/payments${qs ? `?${qs}` : ''}`);
128
+ return page.items;
129
+ },
130
+ },
131
+ {
132
+ name: 'get_payment_stats_by_app',
133
+ description: 'Billing/payment health per Application over the last 30 days: succeeded/failed/pending/refunded ' +
134
+ 'counts, success rate, and SUCCEEDED volume (volumeCents, smallest currency unit). Use this for ' +
135
+ '"which apps are earning / failing payments" questions. (The richer per-app Billing Overview — MRR, ' +
136
+ "12-month revenue series — lives at the panel-session-only endpoint /api/v1/tenant/applications/:id/billing/stats and isn't reachable with this server's credentials.)",
137
+ inputSchema: PaymentStatsByAppArgs,
138
+ execute: (c) => c.request('GET', '/api/v1/admin/metrics/payments-by-app'),
139
+ },
140
+ {
141
+ name: 'mint_api_key',
142
+ description: 'WRITE: Mint a new secret API key (rp_live_…/rp_test_…) for an Application. The raw key is returned exactly ONCE — surface it to the user immediately and tell them it cannot be recovered. ' +
143
+ 'Authenticates as a SCOPED operator via RELIPAY_OPERATOR_TOKEN (not the admin key); the token must carry the `keys:mint` scope and belong to the workspace that owns the Application, or the call is rejected. ' +
144
+ 'Use this only when the user explicitly asks to create/mint an API key.',
145
+ inputSchema: MintApiKeyArgs,
146
+ execute: (c, args) => c.requestAsOperator('POST', `/api/v1/tenant/operator/applications/${encodeURIComponent(args.applicationId)}/api-keys`, {
147
+ name: args.name,
148
+ ...(args.mode !== undefined ? { mode: args.mode } : {}),
149
+ ...(args.scopes !== undefined ? { scopes: args.scopes } : {}),
150
+ }),
151
+ },
152
+ ];
153
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@rekey.dev/mcp",
3
+ "version": "1.1.2",
4
+ "license": "MIT",
5
+ "description": "Model Context Protocol server for Rekey — lets Claude Desktop / Cursor / Claude Code introspect a deployment.",
6
+ "type": "module",
7
+ "bin": {
8
+ "rekey-mcp": "./dist/index.js"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "AGENTS.md",
21
+ "README.md"
22
+ ],
23
+ "keywords": [
24
+ "rekey",
25
+ "mcp",
26
+ "model-context-protocol",
27
+ "ai"
28
+ ],
29
+ "dependencies": {
30
+ "@modelcontextprotocol/sdk": "^1.0.4",
31
+ "zod": "^3.23.8",
32
+ "@rekey.dev/shared-types": "1.1.2"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^22.9.0",
36
+ "typescript": "^5.6.3",
37
+ "vitest": "^2.1.5"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "scripts": {
43
+ "build": "tsc -p tsconfig.json && chmod +x dist/index.js",
44
+ "typecheck": "tsc -p tsconfig.json --noEmit",
45
+ "lint": "echo \"(eslint not yet wired)\"",
46
+ "test": "vitest run"
47
+ }
48
+ }