@deeprelay/mcp 0.0.1 → 0.1.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 +194 -1
- package/dist/auth.d.ts +52 -0
- package/dist/auth.js +122 -0
- package/dist/auth.js.map +1 -0
- package/dist/errors.d.ts +83 -0
- package/dist/errors.js +307 -0
- package/dist/errors.js.map +1 -0
- package/dist/guardrail/budget.d.ts +95 -0
- package/dist/guardrail/budget.js +225 -0
- package/dist/guardrail/budget.js.map +1 -0
- package/dist/guardrail/elicitation.d.ts +12 -0
- package/dist/guardrail/elicitation.js +79 -0
- package/dist/guardrail/elicitation.js.map +1 -0
- package/dist/guardrail/estimate.d.ts +67 -0
- package/dist/guardrail/estimate.js +171 -0
- package/dist/guardrail/estimate.js.map +1 -0
- package/dist/guardrail/handshake.d.ts +32 -0
- package/dist/guardrail/handshake.js +164 -0
- package/dist/guardrail/handshake.js.map +1 -0
- package/dist/guardrail/types.d.ts +61 -0
- package/dist/guardrail/types.js +10 -0
- package/dist/guardrail/types.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/roster.d.ts +36 -0
- package/dist/roster.js +130 -0
- package/dist/roster.js.map +1 -0
- package/dist/tools/crypto-deposit-gate.d.ts +21 -0
- package/dist/tools/crypto-deposit-gate.js +91 -0
- package/dist/tools/crypto-deposit-gate.js.map +1 -0
- package/dist/tools/index.d.ts +16 -0
- package/dist/tools/index.js +108 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/readonly/define.d.ts +32 -0
- package/dist/tools/readonly/define.js +64 -0
- package/dist/tools/readonly/define.js.map +1 -0
- package/dist/tools/readonly/index.d.ts +7 -0
- package/dist/tools/readonly/index.js +286 -0
- package/dist/tools/readonly/index.js.map +1 -0
- package/dist/tools/readonly/usage.d.ts +29 -0
- package/dist/tools/readonly/usage.js +126 -0
- package/dist/tools/readonly/usage.js.map +1 -0
- package/dist/tools/readonly/video-reads.d.ts +11 -0
- package/dist/tools/readonly/video-reads.js +170 -0
- package/dist/tools/readonly/video-reads.js.map +1 -0
- package/dist/tools/result.d.ts +22 -0
- package/dist/tools/result.js +63 -0
- package/dist/tools/result.js.map +1 -0
- package/dist/tools/spend/define.d.ts +93 -0
- package/dist/tools/spend/define.js +286 -0
- package/dist/tools/spend/define.js.map +1 -0
- package/dist/tools/spend/index.d.ts +11 -0
- package/dist/tools/spend/index.js +163 -0
- package/dist/tools/spend/index.js.map +1 -0
- package/dist/tools/spend/media.d.ts +9 -0
- package/dist/tools/spend/media.js +164 -0
- package/dist/tools/spend/media.js.map +1 -0
- package/dist/tools/spend/pricing.d.ts +17 -0
- package/dist/tools/spend/pricing.js +58 -0
- package/dist/tools/spend/pricing.js.map +1 -0
- package/dist/tools/types.d.ts +45 -0
- package/dist/tools/types.js +3 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/write/define.d.ts +60 -0
- package/dist/tools/write/define.js +110 -0
- package/dist/tools/write/define.js.map +1 -0
- package/dist/tools/write/index.d.ts +12 -0
- package/dist/tools/write/index.js +159 -0
- package/dist/tools/write/index.js.map +1 -0
- package/dist/tools/write/payment-links.d.ts +3 -0
- package/dist/tools/write/payment-links.js +161 -0
- package/dist/tools/write/payment-links.js.map +1 -0
- package/dist/tools/write/spending-limit.d.ts +2 -0
- package/dist/tools/write/spending-limit.js +229 -0
- package/dist/tools/write/spending-limit.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.js +36 -0
- package/dist/version.js.map +1 -0
- package/package.json +39 -3
package/README.md
CHANGED
|
@@ -1 +1,194 @@
|
|
|
1
|
-
|
|
1
|
+
# deeprelay MCP server
|
|
2
|
+
|
|
3
|
+
This server connects Claude Code, Claude Desktop, Cursor, or any other
|
|
4
|
+
[MCP](https://modelcontextprotocol.io) client straight to the deeprelay
|
|
5
|
+
serverless inference API. The agent can browse models and their prices, run
|
|
6
|
+
chat, embeddings, image and video generation inside a budget you approve, read
|
|
7
|
+
your balance and usage, manage webhook endpoints, lower or clear your spending
|
|
8
|
+
limit, and create checkout or billing links, all without leaving the editor.
|
|
9
|
+
|
|
10
|
+
**29 tools in three tiers: reads are free, writes confirm once, money tools
|
|
11
|
+
quote a price and require explicit confirmation.** The server enforces these
|
|
12
|
+
rules, so they do not depend on the agent behaving well.
|
|
13
|
+
|
|
14
|
+
This is a **local stdio server**. Your client spawns it as a subprocess and
|
|
15
|
+
talks JSON-RPC over stdin/stdout. Nothing is hosted or proxied: your API key
|
|
16
|
+
stays on your machine, and the server calls `https://api.deeprelay.ai/v1`
|
|
17
|
+
directly.
|
|
18
|
+
|
|
19
|
+
Full documentation: **<https://deeprelay.ai/docs/mcp>**
|
|
20
|
+
|
|
21
|
+
## Requirements
|
|
22
|
+
|
|
23
|
+
- Node 22 or newer
|
|
24
|
+
- A deeprelay API key, created in the dashboard
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
The server runs with `npx -y @deeprelay/mcp@latest`. Your client launches it
|
|
29
|
+
for you.
|
|
30
|
+
|
|
31
|
+
### Claude Code: `.mcp.json`
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"mcpServers": {
|
|
36
|
+
"deeprelay": {
|
|
37
|
+
"type": "stdio",
|
|
38
|
+
"command": "npx",
|
|
39
|
+
"args": ["-y", "@deeprelay/mcp@latest"],
|
|
40
|
+
"env": {
|
|
41
|
+
"DEEPRELAY_API_KEY": "${DEEPRELAY_API_KEY}"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Claude Code fills in `${DEEPRELAY_API_KEY}` from your shell when it spawns the
|
|
49
|
+
server, so the key stays out of the file and out of version control.
|
|
50
|
+
|
|
51
|
+
### Claude Desktop: `claude_desktop_config.json`
|
|
52
|
+
|
|
53
|
+
Claude Desktop does not expand shell variables, so write the value literally:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"deeprelay": {
|
|
59
|
+
"command": "npx",
|
|
60
|
+
"args": ["-y", "@deeprelay/mcp@latest"],
|
|
61
|
+
"env": {
|
|
62
|
+
"DEEPRELAY_API_KEY": "deeprelay_live_..."
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The file is at `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
70
|
+
on macOS and `%APPDATA%\Claude\claude_desktop_config.json` on Windows. Restart
|
|
71
|
+
the app after editing it.
|
|
72
|
+
|
|
73
|
+
For Cursor and other clients, see <https://deeprelay.ai/docs/mcp>.
|
|
74
|
+
|
|
75
|
+
Restart your client, then ask the agent to run `deeprelay_auth_status`. If the
|
|
76
|
+
key is set up correctly, it returns your balance, spending limit and session
|
|
77
|
+
budget.
|
|
78
|
+
|
|
79
|
+
## Configuration
|
|
80
|
+
|
|
81
|
+
| Variable | Required | Meaning |
|
|
82
|
+
| --- | --- | --- |
|
|
83
|
+
| `DEEPRELAY_API_KEY` | yes | The API key. Put it in the client's `env` block only; it must never be passed in args/argv, where it would show up in `ps` output and shell history. |
|
|
84
|
+
| `DEEPRELAY_API_BASE` | no | Base URL override, for example `https://api.demo.deeprelay.ai/v1`. Unset means production (`https://api.deeprelay.ai/v1`). |
|
|
85
|
+
|
|
86
|
+
Startup is **offline**. The server checks that the key is present and that the
|
|
87
|
+
base URL is valid, and makes no network call at boot. A bad key surfaces as a
|
|
88
|
+
clear error on the first tool call. The key is never echoed in a result, an
|
|
89
|
+
error or a confirmation prompt.
|
|
90
|
+
|
|
91
|
+
**Use a dedicated key** for agent work, so you can revoke it on its own and see
|
|
92
|
+
its usage separately. A `read_only` key covers all 17 free tools and cannot
|
|
93
|
+
write or spend. Checkout, the billing portal and spending-limit changes also
|
|
94
|
+
need the org-admin role. **No key scope limits how much a key can spend.** Your
|
|
95
|
+
organization's spending limit and the session budget below do.
|
|
96
|
+
|
|
97
|
+
## The three tiers
|
|
98
|
+
|
|
99
|
+
| Tier | Tools | What happens before it runs |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| Free, read-only | 17 | Nothing. No confirmation, no charge, no change. |
|
|
102
|
+
| Writes: single confirmation | 8 | One confirmation. The first call describes the change and returns a `confirmation_token`. The second call, with the same arguments plus the token, runs it. |
|
|
103
|
+
| Money tools | 4 | A price. Priced from the catalog, then checked against the per-call ceiling and the session budget. |
|
|
104
|
+
|
|
105
|
+
Tokens are single-use, expire after five minutes, and are bound to the exact
|
|
106
|
+
arguments that were quoted. No argument or instruction can replace a token.
|
|
107
|
+
Where the client supports MCP elicitation, the server asks you directly and
|
|
108
|
+
your answer goes through the same token.
|
|
109
|
+
|
|
110
|
+
## Money tools: one session budget
|
|
111
|
+
|
|
112
|
+
Chat, embeddings, image and video share **one session budget**, and a session is
|
|
113
|
+
one server process.
|
|
114
|
+
|
|
115
|
+
- The first money call asks you to approve a **$5.00** session budget. The agent
|
|
116
|
+
can propose a different amount with `session_budget_usd` (**$1–$100**).
|
|
117
|
+
Confirming approves the budget and runs the call.
|
|
118
|
+
- Calls inside the budget then run with no further prompt.
|
|
119
|
+
- Any single call estimated above the **$0.50 per-call ceiling** is confirmed on
|
|
120
|
+
its own. The ceiling is checked before the budget. A model with no published
|
|
121
|
+
rate is treated the same way and is never assumed to be free.
|
|
122
|
+
- When the budget is spent, the next call asks for a new budget.
|
|
123
|
+
|
|
124
|
+
Estimates are reserved before each call and replaced by the actual cost
|
|
125
|
+
afterwards. A failed call releases its reservation.
|
|
126
|
+
|
|
127
|
+
**Video is quoted as a floor, not a cap.** `deeprelay_videos_create` reserves
|
|
128
|
+
the floor price. When `deeprelay_videos_get` or `deeprelay_videos_list` sees the
|
|
129
|
+
job completed, the reservation settles at the actual `cost_cents`. Failed,
|
|
130
|
+
cancelled or expired jobs are not billed. If the server exits before the job
|
|
131
|
+
finishes, the reservation is dropped and the real charge appears in
|
|
132
|
+
`deeprelay_usage_get` and `deeprelay_balance_get`.
|
|
133
|
+
|
|
134
|
+
> Do not run a spend-capable agent with the client's own approval prompts
|
|
135
|
+
> disabled. The client could then accept confirmations for you. Set an
|
|
136
|
+
> organization spending limit as the ceiling that lasts across sessions.
|
|
137
|
+
|
|
138
|
+
## Writes: one confirmation
|
|
139
|
+
|
|
140
|
+
- `deeprelay_webhooks_create` states that deeprelay will **send event data to
|
|
141
|
+
this URL**. The signing secret is returned once and can never be read again.
|
|
142
|
+
- `deeprelay_spending_limit_update` is **lower-only**: a raise is refused before
|
|
143
|
+
any confirmation exists. Raise a limit in the dashboard.
|
|
144
|
+
- `deeprelay_spending_limit_clear` **removes the limit entirely**, after one
|
|
145
|
+
confirmation that names the limit and says inference is then bounded only by
|
|
146
|
+
your balance. With no limit set it reports "nothing to clear" and changes
|
|
147
|
+
nothing. From the agent's side a limit can be lowered or cleared, never
|
|
148
|
+
raised.
|
|
149
|
+
- `deeprelay_subscription_checkout`, `deeprelay_billing_portal` and
|
|
150
|
+
`deeprelay_deposits_create_crypto` **only create links** (or a deposit
|
|
151
|
+
address). They never charge. Nothing is paid until you complete the step
|
|
152
|
+
yourself.
|
|
153
|
+
- `deeprelay_deposits_create_crypto` is listed only on accounts with crypto
|
|
154
|
+
deposits enabled. It stays hidden at startup and appears once the account is
|
|
155
|
+
shown to have the feature.
|
|
156
|
+
|
|
157
|
+
## Tools
|
|
158
|
+
|
|
159
|
+
**Free, read-only (17):** `deeprelay_auth_status`, `deeprelay_health`,
|
|
160
|
+
`deeprelay_models_list`, `deeprelay_models_get`,
|
|
161
|
+
`deeprelay_inference_preflight`, `deeprelay_usage_get`,
|
|
162
|
+
`deeprelay_balance_get`, `deeprelay_spending_limit_get`,
|
|
163
|
+
`deeprelay_subscription_get`, `deeprelay_deposits_list`,
|
|
164
|
+
`deeprelay_deposits_get`, `deeprelay_referrals_get`,
|
|
165
|
+
`deeprelay_webhooks_list`, `deeprelay_webhooks_get`, `deeprelay_videos_list`,
|
|
166
|
+
`deeprelay_videos_get`, `deeprelay_videos_content`.
|
|
167
|
+
|
|
168
|
+
**Writes: single confirmation (8):** `deeprelay_webhooks_create`,
|
|
169
|
+
`deeprelay_webhooks_delete`, `deeprelay_spending_limit_update`,
|
|
170
|
+
`deeprelay_spending_limit_clear`, `deeprelay_subscription_checkout`, `deeprelay_billing_portal`,
|
|
171
|
+
`deeprelay_deposits_create_crypto`, `deeprelay_videos_cancel`.
|
|
172
|
+
|
|
173
|
+
**Money (4):** `deeprelay_chat_completion`, `deeprelay_embeddings_create`,
|
|
174
|
+
`deeprelay_images_generate`, `deeprelay_videos_create`.
|
|
175
|
+
|
|
176
|
+
`deeprelay_health` calls the unauthenticated liveness endpoint, so it answers
|
|
177
|
+
even when the key is wrong or revoked; the server itself still needs
|
|
178
|
+
`DEEPRELAY_API_KEY` set to start. `deeprelay_usage_get` reports serverless
|
|
179
|
+
inference usage: optional `modality` (`chat`, `image`, `video`, `embedding`),
|
|
180
|
+
`model`, `bucket`, `start` and `end`; with no `modality` or `model` it covers
|
|
181
|
+
all four modalities and returns `total_cost_cents` plus per-modality subtotals.
|
|
182
|
+
`deeprelay_videos_content` saves the MP4
|
|
183
|
+
to a local file (under the system temp directory by default, or an absolute
|
|
184
|
+
`output_path`) instead of returning it inline.
|
|
185
|
+
|
|
186
|
+
Per-tool arguments and the full guide are at <https://deeprelay.ai/docs/mcp>.
|
|
187
|
+
|
|
188
|
+
## Support
|
|
189
|
+
|
|
190
|
+
See <https://deeprelay.ai/docs>.
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
Apache-2.0
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Configuration } from "@deeprelay/sdk";
|
|
2
|
+
import type { Env } from "./tools/types.js";
|
|
3
|
+
export type { Env };
|
|
4
|
+
/** The only source of the API credential. */
|
|
5
|
+
export declare const API_KEY_ENV = "DEEPRELAY_API_KEY";
|
|
6
|
+
/** Optional base-URL override (demo, staging, self-hosted). */
|
|
7
|
+
export declare const API_BASE_ENV = "DEEPRELAY_API_BASE";
|
|
8
|
+
/** The default API base. Must stay production (invariant 3). */
|
|
9
|
+
export declare const PROD_API_BASE = "https://api.deeprelay.ai/v1";
|
|
10
|
+
/** A configuration problem found before any request is made. */
|
|
11
|
+
export declare class StartupConfigError extends Error {
|
|
12
|
+
/** The concrete fix, printed under the problem line. */
|
|
13
|
+
readonly remedy: string;
|
|
14
|
+
constructor(message: string, remedy: string);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Return the API key from the environment, or throw StartupConfigError.
|
|
18
|
+
*
|
|
19
|
+
* Presence only: keys are opaque, so there is no format probe and no network
|
|
20
|
+
* call. The value is returned verbatim and is never logged or interpolated.
|
|
21
|
+
*/
|
|
22
|
+
export declare function requireApiKey(env?: Env): string;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the API base URL.
|
|
25
|
+
*
|
|
26
|
+
* Unset or blank resolves to PROD_API_BASE. An override must parse as an
|
|
27
|
+
* absolute http(s) URL; trailing slashes are trimmed so path joins in the SDK
|
|
28
|
+
* never produce `//`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveBaseUrl(env?: Env): string;
|
|
31
|
+
/**
|
|
32
|
+
* Build the single SDK Configuration every tool uses.
|
|
33
|
+
*
|
|
34
|
+
* The SDK owns turning accessToken into the bearer header. The User-Agent set
|
|
35
|
+
* here overrides the SDK's own `deeprelay-sdk-ts/<v>` default, because the
|
|
36
|
+
* SDK merges caller headers over its defaults.
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildConfiguration(env?: Env, options?: BuildConfigurationOptions): Configuration;
|
|
39
|
+
export interface BuildConfigurationOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Build a keyless Configuration when DEEPRELAY_API_KEY is unset. Only the
|
|
42
|
+
* unauthenticated liveness probe (deeprelay_health) sets this, so an agent
|
|
43
|
+
* can tell "the API is up" apart from "my key is missing". A set key is
|
|
44
|
+
* still attached, and a bad base URL still throws.
|
|
45
|
+
*/
|
|
46
|
+
allowMissingKey?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Render a startup failure for stderr: the problem on the first line and the
|
|
50
|
+
* remedy on the second. Always redacted, never a stack trace.
|
|
51
|
+
*/
|
|
52
|
+
export declare function describeStartupError(err: unknown, env?: Env): string;
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StartupConfigError = exports.PROD_API_BASE = exports.API_BASE_ENV = exports.API_KEY_ENV = void 0;
|
|
4
|
+
exports.requireApiKey = requireApiKey;
|
|
5
|
+
exports.resolveBaseUrl = resolveBaseUrl;
|
|
6
|
+
exports.buildConfiguration = buildConfiguration;
|
|
7
|
+
exports.describeStartupError = describeStartupError;
|
|
8
|
+
// Startup configuration and the one API-client construction site
|
|
9
|
+
// (Phase 104, D-12: authored fresh; D-15: @deeprelay/sdk is the client).
|
|
10
|
+
//
|
|
11
|
+
// Invariants this module owns:
|
|
12
|
+
//
|
|
13
|
+
// 1. Boot performs no I/O. The key is presence-checked and the base URL is
|
|
14
|
+
// parsed, and that is all. A network probe at startup would turn a
|
|
15
|
+
// transient blip into "the server will not start", which an operator
|
|
16
|
+
// cannot tell apart from a bad key. A bad key surfaces instead as a mapped
|
|
17
|
+
// 401 on the first real call (errors.ts).
|
|
18
|
+
// 2. The key is read from the environment only: DEEPRELAY_API_KEY. It is
|
|
19
|
+
// never taken from argv (it would show up in `ps` and shell history) and
|
|
20
|
+
// never read from a credentials file on disk.
|
|
21
|
+
// 3. Unset DEEPRELAY_API_BASE means production. A customer package that
|
|
22
|
+
// quietly defaulted to a non-production stack would appear to work while
|
|
23
|
+
// billing the wrong account.
|
|
24
|
+
// 4. Every tool builds its client through buildConfiguration(). It is the only
|
|
25
|
+
// place in the package that constructs a Configuration, so the
|
|
26
|
+
// `deeprelay-mcp/<version>` User-Agent is stamped package-wide and no tool
|
|
27
|
+
// can go out under the wrapped SDK's own default.
|
|
28
|
+
// 5. Every message this module produces goes through redactSecrets().
|
|
29
|
+
const sdk_1 = require("@deeprelay/sdk");
|
|
30
|
+
const errors_js_1 = require("./errors.js");
|
|
31
|
+
const version_js_1 = require("./version.js");
|
|
32
|
+
/** The only source of the API credential. */
|
|
33
|
+
exports.API_KEY_ENV = "DEEPRELAY_API_KEY";
|
|
34
|
+
/** Optional base-URL override (demo, staging, self-hosted). */
|
|
35
|
+
exports.API_BASE_ENV = "DEEPRELAY_API_BASE";
|
|
36
|
+
/** The default API base. Must stay production (invariant 3). */
|
|
37
|
+
exports.PROD_API_BASE = "https://api.deeprelay.ai/v1";
|
|
38
|
+
/** A configuration problem found before any request is made. */
|
|
39
|
+
class StartupConfigError extends Error {
|
|
40
|
+
/** The concrete fix, printed under the problem line. */
|
|
41
|
+
remedy;
|
|
42
|
+
constructor(message, remedy) {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = "StartupConfigError";
|
|
45
|
+
this.remedy = remedy;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.StartupConfigError = StartupConfigError;
|
|
49
|
+
const MISSING_KEY_REMEDY = `Set ${exports.API_KEY_ENV} in the "env" block of this server's entry in your MCP client configuration ` +
|
|
50
|
+
`(create a key at deeprelay.ai; a dedicated key for agent use can be revoked on its own).`;
|
|
51
|
+
const BAD_BASE_REMEDY = `Set ${exports.API_BASE_ENV} to an absolute http(s) URL such as https://api.demo.deeprelay.ai/v1, ` +
|
|
52
|
+
`or unset it to use production (${exports.PROD_API_BASE}).`;
|
|
53
|
+
/**
|
|
54
|
+
* Return the API key from the environment, or throw StartupConfigError.
|
|
55
|
+
*
|
|
56
|
+
* Presence only: keys are opaque, so there is no format probe and no network
|
|
57
|
+
* call. The value is returned verbatim and is never logged or interpolated.
|
|
58
|
+
*/
|
|
59
|
+
function requireApiKey(env = process.env) {
|
|
60
|
+
const key = env[exports.API_KEY_ENV];
|
|
61
|
+
if (key === undefined || key.trim() === "") {
|
|
62
|
+
throw new StartupConfigError(`${exports.API_KEY_ENV} is not set.`, MISSING_KEY_REMEDY);
|
|
63
|
+
}
|
|
64
|
+
return key;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the API base URL.
|
|
68
|
+
*
|
|
69
|
+
* Unset or blank resolves to PROD_API_BASE. An override must parse as an
|
|
70
|
+
* absolute http(s) URL; trailing slashes are trimmed so path joins in the SDK
|
|
71
|
+
* never produce `//`.
|
|
72
|
+
*/
|
|
73
|
+
function resolveBaseUrl(env = process.env) {
|
|
74
|
+
const raw = env[exports.API_BASE_ENV]?.trim();
|
|
75
|
+
if (raw === undefined || raw === "") {
|
|
76
|
+
return exports.PROD_API_BASE;
|
|
77
|
+
}
|
|
78
|
+
let url;
|
|
79
|
+
try {
|
|
80
|
+
url = new URL(raw);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
throw new StartupConfigError(`${exports.API_BASE_ENV} is not a valid absolute URL.`, BAD_BASE_REMEDY);
|
|
84
|
+
}
|
|
85
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
86
|
+
throw new StartupConfigError(`${exports.API_BASE_ENV} must use http or https (got ${url.protocol.replace(/:$/, "")}).`, BAD_BASE_REMEDY);
|
|
87
|
+
}
|
|
88
|
+
return raw.replace(/\/+$/, "");
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Build the single SDK Configuration every tool uses.
|
|
92
|
+
*
|
|
93
|
+
* The SDK owns turning accessToken into the bearer header. The User-Agent set
|
|
94
|
+
* here overrides the SDK's own `deeprelay-sdk-ts/<v>` default, because the
|
|
95
|
+
* SDK merges caller headers over its defaults.
|
|
96
|
+
*/
|
|
97
|
+
function buildConfiguration(env = process.env, options = {}) {
|
|
98
|
+
const accessToken = options.allowMissingKey === true ? optionalApiKey(env) : requireApiKey(env);
|
|
99
|
+
const basePath = resolveBaseUrl(env);
|
|
100
|
+
return new sdk_1.Configuration({
|
|
101
|
+
...(accessToken === undefined ? {} : { accessToken }),
|
|
102
|
+
basePath,
|
|
103
|
+
headers: { "User-Agent": `deeprelay-mcp/${(0, version_js_1.readVersion)()}` },
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
function optionalApiKey(env) {
|
|
107
|
+
const key = env[exports.API_KEY_ENV];
|
|
108
|
+
return key === undefined || key.trim() === "" ? undefined : key;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Render a startup failure for stderr: the problem on the first line and the
|
|
112
|
+
* remedy on the second. Always redacted, never a stack trace.
|
|
113
|
+
*/
|
|
114
|
+
function describeStartupError(err, env = process.env) {
|
|
115
|
+
if (err instanceof StartupConfigError) {
|
|
116
|
+
return (0, errors_js_1.redactSecrets)(`deeprelay-mcp: ${err.message}\n${err.remedy}`, env);
|
|
117
|
+
}
|
|
118
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
119
|
+
return (0, errors_js_1.redactSecrets)(`deeprelay-mcp: failed to start: ${message.replace(/\s+/g, " ").trim()}\n` +
|
|
120
|
+
`Check the ${exports.API_KEY_ENV} and ${exports.API_BASE_ENV} settings in your MCP client configuration.`, env);
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":";;;AAgEA,sCAMC;AASD,wCAmBC;AASD,gDAQC;AAqBD,oDAUC;AAlJD,iEAAiE;AACjE,yEAAyE;AACzE,EAAE;AACF,+BAA+B;AAC/B,EAAE;AACF,4EAA4E;AAC5E,uEAAuE;AACvE,yEAAyE;AACzE,+EAA+E;AAC/E,8CAA8C;AAC9C,0EAA0E;AAC1E,6EAA6E;AAC7E,kDAAkD;AAClD,yEAAyE;AACzE,6EAA6E;AAC7E,iCAAiC;AACjC,gFAAgF;AAChF,mEAAmE;AACnE,+EAA+E;AAC/E,sDAAsD;AACtD,uEAAuE;AACvE,wCAA+C;AAE/C,2CAA4C;AAE5C,6CAA2C;AAI3C,6CAA6C;AAChC,QAAA,WAAW,GAAG,mBAAmB,CAAC;AAE/C,+DAA+D;AAClD,QAAA,YAAY,GAAG,oBAAoB,CAAC;AAEjD,gEAAgE;AACnD,QAAA,aAAa,GAAG,6BAA6B,CAAC;AAE3D,gEAAgE;AAChE,MAAa,kBAAmB,SAAQ,KAAK;IAC3C,wDAAwD;IAC/C,MAAM,CAAS;IAExB,YAAY,OAAe,EAAE,MAAc;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AATD,gDASC;AAED,MAAM,kBAAkB,GACtB,OAAO,mBAAW,8EAA8E;IAChG,0FAA0F,CAAC;AAE7F,MAAM,eAAe,GACnB,OAAO,oBAAY,wEAAwE;IAC3F,kCAAkC,qBAAa,IAAI,CAAC;AAEtD;;;;;GAKG;AACH,SAAgB,aAAa,CAAC,MAAW,OAAO,CAAC,GAAG;IAClD,MAAM,GAAG,GAAG,GAAG,CAAC,mBAAW,CAAC,CAAC;IAC7B,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,kBAAkB,CAAC,GAAG,mBAAW,cAAc,EAAE,kBAAkB,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,cAAc,CAAC,MAAW,OAAO,CAAC,GAAG;IACnD,MAAM,GAAG,GAAG,GAAG,CAAC,oBAAY,CAAC,EAAE,IAAI,EAAE,CAAC;IACtC,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QACpC,OAAO,qBAAa,CAAC;IACvB,CAAC;IAED,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,kBAAkB,CAAC,GAAG,oBAAY,+BAA+B,EAAE,eAAe,CAAC,CAAC;IAChG,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAC1D,MAAM,IAAI,kBAAkB,CAC1B,GAAG,oBAAY,gCAAgC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EACjF,eAAe,CAChB,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,kBAAkB,CAAC,MAAW,OAAO,CAAC,GAAG,EAAE,UAAqC,EAAE;IAChG,MAAM,WAAW,GAAG,OAAO,CAAC,eAAe,KAAK,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAChG,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACrC,OAAO,IAAI,mBAAa,CAAC;QACvB,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;QACrD,QAAQ;QACR,OAAO,EAAE,EAAE,YAAY,EAAE,iBAAiB,IAAA,wBAAW,GAAE,EAAE,EAAE;KAC5D,CAAC,CAAC;AACL,CAAC;AAYD,SAAS,cAAc,CAAC,GAAQ;IAC9B,MAAM,GAAG,GAAG,GAAG,CAAC,mBAAW,CAAC,CAAC;IAC7B,OAAO,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,SAAgB,oBAAoB,CAAC,GAAY,EAAE,MAAW,OAAO,CAAC,GAAG;IACvE,IAAI,GAAG,YAAY,kBAAkB,EAAE,CAAC;QACtC,OAAO,IAAA,yBAAa,EAAC,kBAAkB,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,IAAA,yBAAa,EAClB,mCAAmC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI;QACxE,aAAa,mBAAW,QAAQ,oBAAY,6CAA6C,EAC3F,GAAG,CACJ,CAAC;AACJ,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Env } from "./tools/types.js";
|
|
2
|
+
/** What replaces anything credential-shaped. */
|
|
3
|
+
export declare const REDACTED = "[REDACTED]";
|
|
4
|
+
/**
|
|
5
|
+
* Strip credentials out of arbitrary text. Two passes, because either alone
|
|
6
|
+
* leaves a hole:
|
|
7
|
+
*
|
|
8
|
+
* 1. the configured key's literal value, which catches a key of any shape
|
|
9
|
+
* (a placeholder, another vendor's key pasted by mistake);
|
|
10
|
+
* 2. the deeprelay token shape, which catches a DIFFERENT key echoed back by
|
|
11
|
+
* the API, one this process never held and so cannot match by value.
|
|
12
|
+
*/
|
|
13
|
+
export declare function redactSecrets(text: string, env?: Env): string;
|
|
14
|
+
/**
|
|
15
|
+
* Field names whose string VALUES are credential material. Both spellings,
|
|
16
|
+
* because SDK models are camelCase and raw bodies are snake_case. Exact match:
|
|
17
|
+
* a substring rule would also swallow ordinary fields such as `secret_rotated_at`
|
|
18
|
+
* style metadata or URLs the user needs.
|
|
19
|
+
*
|
|
20
|
+
* The webhook create tool reveals the new endpoint's signing secret exactly
|
|
21
|
+
* once through an explicit reveal path in tools/result.ts. Nothing else ever
|
|
22
|
+
* bypasses this set.
|
|
23
|
+
*/
|
|
24
|
+
export declare const CREDENTIAL_KEYS: ReadonlySet<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Return a copy of `value` with every string under a CREDENTIAL_KEYS field
|
|
27
|
+
* replaced by REDACTED, at any depth and inside arrays. The input is never
|
|
28
|
+
* mutated. Non-string credential values (null, absent) are left alone so the
|
|
29
|
+
* agent is not told a secret exists where none does. Class instances such as
|
|
30
|
+
* Date are returned whole rather than flattened.
|
|
31
|
+
*/
|
|
32
|
+
export declare function sanitizeCredentialFields(value: unknown): unknown;
|
|
33
|
+
/**
|
|
34
|
+
* A spend or write tool's quote step refusing to price a request. Throwing is
|
|
35
|
+
* what keeps a guardrail from minting a confirmation token for something that
|
|
36
|
+
* has no quote: no quote, no token, no execution. The mapper keeps this
|
|
37
|
+
* error's own wording.
|
|
38
|
+
*/
|
|
39
|
+
export declare class QuoteUnavailableError extends Error {
|
|
40
|
+
constructor(message: string);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The HTTP status of an SDK error, or undefined for anything that never got a
|
|
44
|
+
* response. Duck-typed as a fallback so a bundling or version skew that breaks
|
|
45
|
+
* instanceof cannot change behaviour. Exported so callers can branch on a
|
|
46
|
+
* status (e.g. a 404 spending limit meaning "none configured") consistently
|
|
47
|
+
* with the mapper.
|
|
48
|
+
*/
|
|
49
|
+
export declare function httpStatusOf(err: unknown): number | undefined;
|
|
50
|
+
/** The parts of an API error body worth keeping. */
|
|
51
|
+
export interface ErrorEnvelope {
|
|
52
|
+
code?: string;
|
|
53
|
+
message?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface MapErrorOptions {
|
|
56
|
+
env?: Env;
|
|
57
|
+
/** A short summary already lifted from the body (see readErrorEnvelope). */
|
|
58
|
+
details?: string;
|
|
59
|
+
/** The API's stable error code, when known. */
|
|
60
|
+
code?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Turn any thrown value into an agent-legible Error. Synchronous so the
|
|
64
|
+
* mapping is directly testable; tool handlers normally call mapSdkErrorAsync,
|
|
65
|
+
* which reads the response body first.
|
|
66
|
+
*
|
|
67
|
+
* `context` names what was being attempted ("listing models"), so a failure
|
|
68
|
+
* several calls deep stays attributable.
|
|
69
|
+
*/
|
|
70
|
+
export declare function mapSdkError(err: unknown, context: string, options?: MapErrorOptions): Error;
|
|
71
|
+
/**
|
|
72
|
+
* Read `{ code, message }` out of an SDK error's response body. Accepts both
|
|
73
|
+
* shapes the API returns: the OpenAI envelope `{ error: { code, message } }`
|
|
74
|
+
* and problem+json `{ code, detail }`. Fails closed: anything that is not
|
|
75
|
+
* recognisably one of those yields an empty envelope, never the raw body.
|
|
76
|
+
* Reads a clone, so the caller's Response is left unconsumed.
|
|
77
|
+
*/
|
|
78
|
+
export declare function readErrorEnvelope(err: unknown): Promise<ErrorEnvelope>;
|
|
79
|
+
/**
|
|
80
|
+
* The funnel tool handlers call: read the body once, then map. A 401 never
|
|
81
|
+
* uses the body (see statusSentence).
|
|
82
|
+
*/
|
|
83
|
+
export declare function mapSdkErrorAsync(err: unknown, context: string, env?: Env): Promise<Error>;
|