@cruxy/cli 0.26.0 → 0.27.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/dist/cli/commands/mcp.js +106 -7
- package/dist/config/credentials.d.ts +9 -0
- package/dist/config/credentials.js +29 -1
- package/dist/config/manager.js +30 -3
- package/dist/config/schema.d.ts +182 -8
- package/dist/config/schema.js +43 -5
- package/dist/errors/constructors.d.ts +29 -0
- package/dist/errors/constructors.js +69 -0
- package/dist/errors/types.d.ts +15 -0
- package/dist/errors/types.js +18 -0
- package/dist/mcp/http-transport.d.ts +89 -0
- package/dist/mcp/http-transport.js +299 -0
- package/dist/mcp/index.d.ts +4 -2
- package/dist/mcp/index.js +3 -1
- package/dist/mcp/service.d.ts +19 -2
- package/dist/mcp/service.js +92 -20
- package/dist/mcp/trust-gate.d.ts +35 -11
- package/dist/mcp/trust-gate.js +87 -22
- package/dist/mcp/trust.d.ts +12 -2
- package/dist/mcp/trust.js +26 -2
- package/dist/mcp/types.d.ts +10 -0
- package/dist/mcp/url-guard.d.ts +48 -0
- package/dist/mcp/url-guard.js +62 -0
- package/dist/net/ip-guard.d.ts +55 -0
- package/dist/net/ip-guard.js +229 -0
- package/dist/render/index.d.ts +1 -0
- package/dist/render/index.js +1 -0
- package/dist/render/motion.d.ts +76 -0
- package/dist/render/motion.js +94 -0
- package/dist/render/tty-renderer.d.ts +17 -3
- package/dist/render/tty-renderer.js +58 -21
- package/dist/web/ssrf.d.ts +8 -22
- package/dist/web/ssrf.js +11 -183
- package/dist/web/types.d.ts +4 -2
- package/package.json +1 -1
package/dist/cli/commands/mcp.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
-
import { loadConfig } from "../../config/index.js";
|
|
4
|
-
import {
|
|
3
|
+
import { loadConfig, writeMcpCredential } from "../../config/index.js";
|
|
4
|
+
import { defaultOnboardingIO } from "../../onboarding/index.js";
|
|
5
|
+
import { interactiveRequired, shouldUseColor } from "../../errors/index.js";
|
|
5
6
|
import { themeForColor } from "../../theme/index.js";
|
|
6
|
-
import { fileMcpTrustStore, fingerprintMcpServers, isMcpTrusted, } from "../../mcp/index.js";
|
|
7
|
+
import { fileMcpTrustStore, fingerprintMcpServers, isMcpTrusted, resolveMcpEndpoints, } from "../../mcp/index.js";
|
|
8
|
+
import { BlockedHostError, defaultResolveHost, HostUnresolvedError, } from "../../net/ip-guard.js";
|
|
7
9
|
import { logger } from "../../utils/logger.js";
|
|
8
10
|
/**
|
|
9
11
|
* `cruxy mcp` — inspect and control MCP server integration (C.27). `list` shows
|
|
@@ -36,11 +38,16 @@ export function mcpCommand() {
|
|
|
36
38
|
for (const [name, cfg] of Object.entries(servers)) {
|
|
37
39
|
logger.print(` ${t.strong(name)} ${t.muted(describeServer(cfg))}`);
|
|
38
40
|
}
|
|
41
|
+
if (trusted && Object.values(servers).some((c) => c.url)) {
|
|
42
|
+
// JC-D: url servers are also bound to their resolved IP set, re-checked at
|
|
43
|
+
// connect; a changed IP set re-gates even though the static status is "trusted".
|
|
44
|
+
logger.print(t.muted("\n note: url servers are re-verified against their trusted IP set on connect"));
|
|
45
|
+
}
|
|
39
46
|
});
|
|
40
47
|
cmd
|
|
41
48
|
.command("trust [path]")
|
|
42
49
|
.description("trust this repo's MCP integrations (they run with your full privileges)")
|
|
43
|
-
.action((target) => {
|
|
50
|
+
.action(async (target) => {
|
|
44
51
|
const t = themeForColor(shouldUseColor(process.stdout));
|
|
45
52
|
const { config } = loadConfig();
|
|
46
53
|
const servers = config.mcp.servers;
|
|
@@ -50,17 +57,91 @@ export function mcpCommand() {
|
|
|
50
57
|
logger.print(t.muted(`no MCP servers configured under ${root}`));
|
|
51
58
|
return;
|
|
52
59
|
}
|
|
53
|
-
|
|
60
|
+
const hasStdio = Object.values(servers).some((c) => c.command);
|
|
61
|
+
const hasUrl = Object.values(servers).some((c) => c.url);
|
|
62
|
+
logger.print(t.danger(t.strong(`trusting ${names.length} MCP server${names.length === 1 ? "" : "s"} for ${root}:`)));
|
|
54
63
|
for (const [name, cfg] of Object.entries(servers)) {
|
|
55
64
|
logger.print(` ${t.strong(name)} ${t.muted(describeServer(cfg))}`);
|
|
56
65
|
}
|
|
66
|
+
if (hasStdio) {
|
|
67
|
+
logger.print(t.muted(" stdio servers run their code UNSANDBOXED with your full privileges."));
|
|
68
|
+
}
|
|
69
|
+
if (hasUrl) {
|
|
70
|
+
logger.print(t.muted(" url servers receive your tool arguments over the network; trust binds\n" +
|
|
71
|
+
" the URL + its current IP set (weaker than a local binary — see docs)."));
|
|
72
|
+
// C.27c: a server that also RECEIVES a credential is a bigger grant —
|
|
73
|
+
// name it so the grant is explicit at trust time.
|
|
74
|
+
for (const [name, cfg] of Object.entries(servers)) {
|
|
75
|
+
if (!cfg.credentialRef && !cfg.headers)
|
|
76
|
+
continue;
|
|
77
|
+
const what = cfg.credentialRef
|
|
78
|
+
? `your "${cfg.credentialRef}" credential`
|
|
79
|
+
: "your configured auth headers";
|
|
80
|
+
logger.print(t.danger(` "${name}" will RECEIVE ${what} on every request.`));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// JC-D: capture + SSRF-validate each url server's endpoint set and bind it
|
|
84
|
+
// into the decision. A refused URL (SSRF/scheme) fails closed — no record.
|
|
85
|
+
let endpoints = {};
|
|
86
|
+
try {
|
|
87
|
+
endpoints = await resolveMcpEndpoints(servers, defaultResolveHost);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
if (err instanceof BlockedHostError ||
|
|
91
|
+
err instanceof HostUnresolvedError) {
|
|
92
|
+
logger.print(t.danger(`refused to trust — a url server could not be validated: ${err.message}`));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
57
97
|
fileMcpTrustStore().record({
|
|
58
98
|
root,
|
|
59
99
|
fingerprint: fingerprintMcpServers(servers),
|
|
60
100
|
at: new Date().toISOString(),
|
|
101
|
+
endpoints,
|
|
61
102
|
});
|
|
62
103
|
logger.print(`${t.success("trusted")} — cruxy will connect these servers for ${root}. ` +
|
|
63
|
-
t.muted("changing the config
|
|
104
|
+
t.muted("changing the config (or a url's IP set) requires re-trusting."));
|
|
105
|
+
});
|
|
106
|
+
cmd
|
|
107
|
+
.command("login <server>")
|
|
108
|
+
.description("set a bearer credential for a remote MCP integration (owner-only in ~/.cruxy)")
|
|
109
|
+
.action(async (server) => {
|
|
110
|
+
const t = themeForColor(shouldUseColor(process.stdout));
|
|
111
|
+
const { config } = loadConfig();
|
|
112
|
+
const cfg = config.mcp.servers[server];
|
|
113
|
+
if (!cfg) {
|
|
114
|
+
logger.print(t.danger(`no MCP server "${server}" is configured (mcp.servers)`));
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (!cfg.url) {
|
|
119
|
+
logger.print(t.danger(`server "${server}" is a stdio server — it has no bearer credential ` +
|
|
120
|
+
"(stdio servers take secrets via `env`)"));
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// The store is keyed by the credential NAME the config references; default
|
|
125
|
+
// to the server id when the config names none.
|
|
126
|
+
const ref = cfg.credentialRef ?? server;
|
|
127
|
+
if (!process.stdin.isTTY) {
|
|
128
|
+
// Never read a secret from a pipe silently — same discipline as onboarding.
|
|
129
|
+
throw interactiveRequired(`a credential for MCP server "${server}"`, [
|
|
130
|
+
"run this in an interactive terminal",
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
133
|
+
const io = defaultOnboardingIO(shouldUseColor(process.stderr));
|
|
134
|
+
io.write(`${t.strong(`credential for "${server}"`)} ${t.muted(`(stored as "${ref}" in ~/.cruxy/credentials.json, 0600)`)}\n`);
|
|
135
|
+
io.write(`${t.muted("paste the bearer token (input hidden): ")}`);
|
|
136
|
+
const token = (await io.readSecret()).trim();
|
|
137
|
+
if (!token) {
|
|
138
|
+
logger.print(t.warning("no token entered — nothing was stored"));
|
|
139
|
+
process.exitCode = 1;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
writeMcpCredential(ref, token);
|
|
143
|
+
logger.print(`${t.success("stored")} — cruxy will send this credential to "${server}" over https only. ` +
|
|
144
|
+
t.muted("re-run to rotate; the value is never shown or logged."));
|
|
64
145
|
});
|
|
65
146
|
cmd
|
|
66
147
|
.command("untrust [path]")
|
|
@@ -83,5 +164,23 @@ function describeServer(cfg) {
|
|
|
83
164
|
if (cfg.command) {
|
|
84
165
|
return `(stdio: ${[cfg.command, ...(cfg.args ?? [])].join(" ")})`;
|
|
85
166
|
}
|
|
86
|
-
|
|
167
|
+
if (!cfg.url)
|
|
168
|
+
return "(no transport)";
|
|
169
|
+
return `(url: ${cfg.url}${describeAuth(cfg)})`;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* A REDACTED auth descriptor for display (C.27c) — names the credential and
|
|
173
|
+
* counts raw headers, but NEVER prints a header value or a token. Display
|
|
174
|
+
* redaction is defense-in-depth; the load-bearing guard is that project config
|
|
175
|
+
* can't carry a value at all.
|
|
176
|
+
*/
|
|
177
|
+
function describeAuth(cfg) {
|
|
178
|
+
const parts = [];
|
|
179
|
+
if (cfg.credentialRef)
|
|
180
|
+
parts.push(`credential: ${cfg.credentialRef}`);
|
|
181
|
+
const headerCount = cfg.headers ? Object.keys(cfg.headers).length : 0;
|
|
182
|
+
if (headerCount > 0) {
|
|
183
|
+
parts.push(`${headerCount} header${headerCount === 1 ? "" : "s"} (redacted)`);
|
|
184
|
+
}
|
|
185
|
+
return parts.length ? `, ${parts.join(", ")}` : "";
|
|
87
186
|
}
|
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
export declare function credentialsPath(): string;
|
|
3
3
|
/** The stored key for `provider`, or `undefined`. Never throws. */
|
|
4
4
|
export declare function readCredential(provider: string, file?: string): string | undefined;
|
|
5
|
+
/**
|
|
6
|
+
* The stored MCP bearer token for credential name `ref`, or `undefined` (C.27c).
|
|
7
|
+
* Reads ONLY this owner-only store — never the environment, never any config file
|
|
8
|
+
* — so a project config that merely NAMES a credential can never widen where the
|
|
9
|
+
* secret is sourced from. Never throws.
|
|
10
|
+
*/
|
|
11
|
+
export declare function readMcpCredential(ref: string, file?: string): string | undefined;
|
|
12
|
+
/** Persist MCP bearer token for credential name `ref`. Same 0600/0700 as keys. */
|
|
13
|
+
export declare function writeMcpCredential(ref: string, token: string, file?: string): void;
|
|
5
14
|
/**
|
|
6
15
|
* Persist `key` for `provider`, merging into any existing store. The file is
|
|
7
16
|
* written `0600` and its directory `0700` so the secret is owner-only — enforced
|
|
@@ -39,12 +39,40 @@ export function readCredential(provider, file = credentialsPath()) {
|
|
|
39
39
|
const key = store?.keys[provider];
|
|
40
40
|
return typeof key === "string" && key !== "" ? key : undefined;
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* The stored MCP bearer token for credential name `ref`, or `undefined` (C.27c).
|
|
44
|
+
* Reads ONLY this owner-only store — never the environment, never any config file
|
|
45
|
+
* — so a project config that merely NAMES a credential can never widen where the
|
|
46
|
+
* secret is sourced from. Never throws.
|
|
47
|
+
*/
|
|
48
|
+
export function readMcpCredential(ref, file = credentialsPath()) {
|
|
49
|
+
const store = readStore(file);
|
|
50
|
+
const token = store?.mcp?.[ref];
|
|
51
|
+
return typeof token === "string" && token !== "" ? token : undefined;
|
|
52
|
+
}
|
|
53
|
+
/** Persist MCP bearer token for credential name `ref`. Same 0600/0700 as keys. */
|
|
54
|
+
export function writeMcpCredential(ref, token, file = credentialsPath()) {
|
|
55
|
+
writeInto(file, (store) => {
|
|
56
|
+
store.mcp ??= {};
|
|
57
|
+
store.mcp[ref] = token;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
42
60
|
/**
|
|
43
61
|
* Persist `key` for `provider`, merging into any existing store. The file is
|
|
44
62
|
* written `0600` and its directory `0700` so the secret is owner-only — enforced
|
|
45
63
|
* with an explicit `chmod` after write (mkdir/write modes are umask-masked).
|
|
46
64
|
*/
|
|
47
65
|
export function writeCredential(provider, key, file = credentialsPath()) {
|
|
66
|
+
writeInto(file, (store) => {
|
|
67
|
+
store.keys[provider] = key;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Merge `mutate` into the store and persist it owner-only: dir `0700`, file
|
|
72
|
+
* `0600`, enforced with an explicit `chmod` after write (mkdir/write modes are
|
|
73
|
+
* umask-masked). The one write path shared by every credential namespace.
|
|
74
|
+
*/
|
|
75
|
+
function writeInto(file, mutate) {
|
|
48
76
|
const dir = dirname(file);
|
|
49
77
|
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
50
78
|
try {
|
|
@@ -55,7 +83,7 @@ export function writeCredential(provider, key, file = credentialsPath()) {
|
|
|
55
83
|
}
|
|
56
84
|
const store = readStore(file) ?? { version: CREDENTIALS_VERSION, keys: {} };
|
|
57
85
|
store.version = CREDENTIALS_VERSION;
|
|
58
|
-
store
|
|
86
|
+
mutate(store);
|
|
59
87
|
writeFileSync(file, JSON.stringify(store, null, 2) + "\n", {
|
|
60
88
|
encoding: "utf8",
|
|
61
89
|
mode: 0o600,
|
package/dist/config/manager.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
-
import { configInvalid, configParse } from "../errors/index.js";
|
|
3
|
+
import { configInvalid, configParse, mcpProjectHeaders, } from "../errors/index.js";
|
|
4
4
|
import { CruxyConfigSchema } from "./schema.js";
|
|
5
5
|
import { globalConfigPath, findProjectConfig } from "./paths.js";
|
|
6
6
|
import { readCredential } from "./credentials.js";
|
|
@@ -36,6 +36,29 @@ function readJsonFile(path) {
|
|
|
36
36
|
throw err;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* §0 credential-safety gate (C.27c): a project-scope config file must not set raw
|
|
41
|
+
* `mcp.servers.<id>.headers`. A live header value is a secret, and this file is a
|
|
42
|
+
* possibly-cloned repo, so a raw header here is REFUSED — loudly, naming the
|
|
43
|
+
* server — never merged and never silently dropped (a silent drop would let the
|
|
44
|
+
* repo believe auth is wired up when it is not). Only user-scope config
|
|
45
|
+
* (`~/.cruxy/config.json`) may carry raw headers; every other file (a discovered
|
|
46
|
+
* project config, or an explicit `--config` that may point into a repo) is
|
|
47
|
+
* project scope. A project config may still NAME a credential via `credentialRef`.
|
|
48
|
+
*/
|
|
49
|
+
function rejectProjectScopeHeaders(obj, file) {
|
|
50
|
+
const mcp = obj.mcp;
|
|
51
|
+
if (!isPlainObject(mcp))
|
|
52
|
+
return;
|
|
53
|
+
const servers = mcp.servers;
|
|
54
|
+
if (!isPlainObject(servers))
|
|
55
|
+
return;
|
|
56
|
+
for (const [id, server] of Object.entries(servers)) {
|
|
57
|
+
if (isPlainObject(server) && "headers" in server) {
|
|
58
|
+
throw mcpProjectHeaders(id, file);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
39
62
|
/** Overrides sourced from environment variables. */
|
|
40
63
|
function envOverrides() {
|
|
41
64
|
const out = {};
|
|
@@ -67,13 +90,17 @@ export function loadConfig(opts = {}) {
|
|
|
67
90
|
sources.global = gPath;
|
|
68
91
|
}
|
|
69
92
|
if (opts.configPath) {
|
|
70
|
-
|
|
93
|
+
const obj = readJsonFile(opts.configPath);
|
|
94
|
+
rejectProjectScopeHeaders(obj, opts.configPath);
|
|
95
|
+
merged = deepMerge(merged, obj);
|
|
71
96
|
sources.explicit = opts.configPath;
|
|
72
97
|
}
|
|
73
98
|
else {
|
|
74
99
|
const pPath = findProjectConfig(opts.cwd);
|
|
75
100
|
if (pPath) {
|
|
76
|
-
|
|
101
|
+
const obj = readJsonFile(pPath);
|
|
102
|
+
rejectProjectScopeHeaders(obj, pPath);
|
|
103
|
+
merged = deepMerge(merged, obj);
|
|
77
104
|
sources.project = pPath;
|
|
78
105
|
}
|
|
79
106
|
}
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -649,13 +649,19 @@ export declare const UsageConfigSchema: z.ZodObject<{
|
|
|
649
649
|
}>;
|
|
650
650
|
export type UsageConfig = z.infer<typeof UsageConfigSchema>;
|
|
651
651
|
/**
|
|
652
|
-
* One MCP server entry
|
|
653
|
-
*
|
|
654
|
-
* one transport must be given.
|
|
655
|
-
*
|
|
656
|
-
* fingerprinted trust decision (
|
|
652
|
+
* One MCP server entry. A `command` (+ optional `args`/`env`) is a stdio server
|
|
653
|
+
* cruxy spawns as a child process (C.27); a `url` names a remote server reached
|
|
654
|
+
* over Streamable HTTP (C.27b). Exactly one transport must be given.
|
|
655
|
+
*
|
|
656
|
+
* Both are gated by an explicit, fingerprinted trust decision (`mcp/trust.ts`),
|
|
657
|
+
* but the escalation differs: a stdio server runs its code UNSANDBOXED with your
|
|
658
|
+
* privileges, while a `url` server runs remotely — cruxy sends it your tool
|
|
659
|
+
* arguments over the network (https + cert-validated + pinned to a public IP; http
|
|
660
|
+
* only for a loopback dev server) and treats its responses as untrusted data. A
|
|
661
|
+
* url's trust also binds its resolved IP set at trust time, so a later IP-set
|
|
662
|
+
* change re-gates (weaker than a local binary fingerprint — see `mcp/types.ts`).
|
|
657
663
|
*/
|
|
658
|
-
export declare const McpServerSchema: z.ZodEffects<z.ZodObject<{
|
|
664
|
+
export declare const McpServerSchema: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodObject<{
|
|
659
665
|
/** stdio transport: the server program to spawn. */
|
|
660
666
|
command: z.ZodOptional<z.ZodString>;
|
|
661
667
|
/** Arguments for `command`. */
|
|
@@ -664,26 +670,78 @@ export declare const McpServerSchema: z.ZodEffects<z.ZodObject<{
|
|
|
664
670
|
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
665
671
|
/** Remote transport: the server URL (mutually exclusive with `command`). */
|
|
666
672
|
url: z.ZodOptional<z.ZodString>;
|
|
673
|
+
/**
|
|
674
|
+
* Remote auth (C.27c, url only): the NAME of a bearer credential, resolved
|
|
675
|
+
* solely from `~/.cruxy/credentials.json` (0600) as `Authorization: Bearer …`.
|
|
676
|
+
* This field holds a NAME, never the secret — a project config may name a
|
|
677
|
+
* credential it does not contain, so the value never lives in-repo. Resolution
|
|
678
|
+
* never consults the environment or any config file. Requires an `https` URL.
|
|
679
|
+
*/
|
|
680
|
+
credentialRef: z.ZodOptional<z.ZodString>;
|
|
681
|
+
/**
|
|
682
|
+
* Remote auth (C.27c, url only): raw request headers sent to the endpoint.
|
|
683
|
+
* A LIVE header value is a secret, so this is accepted ONLY from user-scope
|
|
684
|
+
* config (`~/.cruxy/config.json`) — the loader REJECTS it from project-scope
|
|
685
|
+
* config (a cloned repo must not inject live headers). Prefer `credentialRef`.
|
|
686
|
+
* Requires an `https` URL; reserved framing headers cannot be overridden.
|
|
687
|
+
*/
|
|
688
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
667
689
|
}, "strict", z.ZodTypeAny, {
|
|
668
690
|
args: string[];
|
|
669
691
|
env: Record<string, string>;
|
|
670
692
|
command?: string | undefined;
|
|
693
|
+
credentialRef?: string | undefined;
|
|
694
|
+
url?: string | undefined;
|
|
695
|
+
headers?: Record<string, string> | undefined;
|
|
696
|
+
}, {
|
|
697
|
+
command?: string | undefined;
|
|
698
|
+
credentialRef?: string | undefined;
|
|
699
|
+
url?: string | undefined;
|
|
700
|
+
args?: string[] | undefined;
|
|
701
|
+
env?: Record<string, string> | undefined;
|
|
702
|
+
headers?: Record<string, string> | undefined;
|
|
703
|
+
}>, {
|
|
704
|
+
args: string[];
|
|
705
|
+
env: Record<string, string>;
|
|
706
|
+
command?: string | undefined;
|
|
707
|
+
credentialRef?: string | undefined;
|
|
671
708
|
url?: string | undefined;
|
|
709
|
+
headers?: Record<string, string> | undefined;
|
|
672
710
|
}, {
|
|
673
711
|
command?: string | undefined;
|
|
712
|
+
credentialRef?: string | undefined;
|
|
674
713
|
url?: string | undefined;
|
|
675
714
|
args?: string[] | undefined;
|
|
676
715
|
env?: Record<string, string> | undefined;
|
|
716
|
+
headers?: Record<string, string> | undefined;
|
|
677
717
|
}>, {
|
|
678
718
|
args: string[];
|
|
679
719
|
env: Record<string, string>;
|
|
680
720
|
command?: string | undefined;
|
|
721
|
+
credentialRef?: string | undefined;
|
|
681
722
|
url?: string | undefined;
|
|
723
|
+
headers?: Record<string, string> | undefined;
|
|
682
724
|
}, {
|
|
683
725
|
command?: string | undefined;
|
|
726
|
+
credentialRef?: string | undefined;
|
|
684
727
|
url?: string | undefined;
|
|
685
728
|
args?: string[] | undefined;
|
|
686
729
|
env?: Record<string, string> | undefined;
|
|
730
|
+
headers?: Record<string, string> | undefined;
|
|
731
|
+
}>, {
|
|
732
|
+
args: string[];
|
|
733
|
+
env: Record<string, string>;
|
|
734
|
+
command?: string | undefined;
|
|
735
|
+
credentialRef?: string | undefined;
|
|
736
|
+
url?: string | undefined;
|
|
737
|
+
headers?: Record<string, string> | undefined;
|
|
738
|
+
}, {
|
|
739
|
+
command?: string | undefined;
|
|
740
|
+
credentialRef?: string | undefined;
|
|
741
|
+
url?: string | undefined;
|
|
742
|
+
args?: string[] | undefined;
|
|
743
|
+
env?: Record<string, string> | undefined;
|
|
744
|
+
headers?: Record<string, string> | undefined;
|
|
687
745
|
}>;
|
|
688
746
|
export type McpServerConfig = z.infer<typeof McpServerSchema>;
|
|
689
747
|
/**
|
|
@@ -704,7 +762,7 @@ export declare const McpConfigSchema: z.ZodObject<{
|
|
|
704
762
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
705
763
|
/** Named MCP servers, keyed by a short server id used as the tool prefix
|
|
706
764
|
* (`mcp__<server>__<tool>`) and in the trust prompt. */
|
|
707
|
-
servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
|
|
765
|
+
servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodObject<{
|
|
708
766
|
/** stdio transport: the server program to spawn. */
|
|
709
767
|
command: z.ZodOptional<z.ZodString>;
|
|
710
768
|
/** Arguments for `command`. */
|
|
@@ -713,26 +771,78 @@ export declare const McpConfigSchema: z.ZodObject<{
|
|
|
713
771
|
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
714
772
|
/** Remote transport: the server URL (mutually exclusive with `command`). */
|
|
715
773
|
url: z.ZodOptional<z.ZodString>;
|
|
774
|
+
/**
|
|
775
|
+
* Remote auth (C.27c, url only): the NAME of a bearer credential, resolved
|
|
776
|
+
* solely from `~/.cruxy/credentials.json` (0600) as `Authorization: Bearer …`.
|
|
777
|
+
* This field holds a NAME, never the secret — a project config may name a
|
|
778
|
+
* credential it does not contain, so the value never lives in-repo. Resolution
|
|
779
|
+
* never consults the environment or any config file. Requires an `https` URL.
|
|
780
|
+
*/
|
|
781
|
+
credentialRef: z.ZodOptional<z.ZodString>;
|
|
782
|
+
/**
|
|
783
|
+
* Remote auth (C.27c, url only): raw request headers sent to the endpoint.
|
|
784
|
+
* A LIVE header value is a secret, so this is accepted ONLY from user-scope
|
|
785
|
+
* config (`~/.cruxy/config.json`) — the loader REJECTS it from project-scope
|
|
786
|
+
* config (a cloned repo must not inject live headers). Prefer `credentialRef`.
|
|
787
|
+
* Requires an `https` URL; reserved framing headers cannot be overridden.
|
|
788
|
+
*/
|
|
789
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
716
790
|
}, "strict", z.ZodTypeAny, {
|
|
717
791
|
args: string[];
|
|
718
792
|
env: Record<string, string>;
|
|
719
793
|
command?: string | undefined;
|
|
794
|
+
credentialRef?: string | undefined;
|
|
720
795
|
url?: string | undefined;
|
|
796
|
+
headers?: Record<string, string> | undefined;
|
|
721
797
|
}, {
|
|
722
798
|
command?: string | undefined;
|
|
799
|
+
credentialRef?: string | undefined;
|
|
723
800
|
url?: string | undefined;
|
|
724
801
|
args?: string[] | undefined;
|
|
725
802
|
env?: Record<string, string> | undefined;
|
|
803
|
+
headers?: Record<string, string> | undefined;
|
|
726
804
|
}>, {
|
|
727
805
|
args: string[];
|
|
728
806
|
env: Record<string, string>;
|
|
729
807
|
command?: string | undefined;
|
|
808
|
+
credentialRef?: string | undefined;
|
|
730
809
|
url?: string | undefined;
|
|
810
|
+
headers?: Record<string, string> | undefined;
|
|
731
811
|
}, {
|
|
732
812
|
command?: string | undefined;
|
|
813
|
+
credentialRef?: string | undefined;
|
|
733
814
|
url?: string | undefined;
|
|
734
815
|
args?: string[] | undefined;
|
|
735
816
|
env?: Record<string, string> | undefined;
|
|
817
|
+
headers?: Record<string, string> | undefined;
|
|
818
|
+
}>, {
|
|
819
|
+
args: string[];
|
|
820
|
+
env: Record<string, string>;
|
|
821
|
+
command?: string | undefined;
|
|
822
|
+
credentialRef?: string | undefined;
|
|
823
|
+
url?: string | undefined;
|
|
824
|
+
headers?: Record<string, string> | undefined;
|
|
825
|
+
}, {
|
|
826
|
+
command?: string | undefined;
|
|
827
|
+
credentialRef?: string | undefined;
|
|
828
|
+
url?: string | undefined;
|
|
829
|
+
args?: string[] | undefined;
|
|
830
|
+
env?: Record<string, string> | undefined;
|
|
831
|
+
headers?: Record<string, string> | undefined;
|
|
832
|
+
}>, {
|
|
833
|
+
args: string[];
|
|
834
|
+
env: Record<string, string>;
|
|
835
|
+
command?: string | undefined;
|
|
836
|
+
credentialRef?: string | undefined;
|
|
837
|
+
url?: string | undefined;
|
|
838
|
+
headers?: Record<string, string> | undefined;
|
|
839
|
+
}, {
|
|
840
|
+
command?: string | undefined;
|
|
841
|
+
credentialRef?: string | undefined;
|
|
842
|
+
url?: string | undefined;
|
|
843
|
+
args?: string[] | undefined;
|
|
844
|
+
env?: Record<string, string> | undefined;
|
|
845
|
+
headers?: Record<string, string> | undefined;
|
|
736
846
|
}>>>;
|
|
737
847
|
/** Fail a server's `initialize` handshake (its tools are skipped) if it does
|
|
738
848
|
* not complete within this many ms. */
|
|
@@ -756,7 +866,9 @@ export declare const McpConfigSchema: z.ZodObject<{
|
|
|
756
866
|
args: string[];
|
|
757
867
|
env: Record<string, string>;
|
|
758
868
|
command?: string | undefined;
|
|
869
|
+
credentialRef?: string | undefined;
|
|
759
870
|
url?: string | undefined;
|
|
871
|
+
headers?: Record<string, string> | undefined;
|
|
760
872
|
}>;
|
|
761
873
|
enabled: boolean;
|
|
762
874
|
maxToolsPerServer: number;
|
|
@@ -767,9 +879,11 @@ export declare const McpConfigSchema: z.ZodObject<{
|
|
|
767
879
|
requestTimeout?: number | undefined;
|
|
768
880
|
servers?: Record<string, {
|
|
769
881
|
command?: string | undefined;
|
|
882
|
+
credentialRef?: string | undefined;
|
|
770
883
|
url?: string | undefined;
|
|
771
884
|
args?: string[] | undefined;
|
|
772
885
|
env?: Record<string, string> | undefined;
|
|
886
|
+
headers?: Record<string, string> | undefined;
|
|
773
887
|
}> | undefined;
|
|
774
888
|
enabled?: boolean | undefined;
|
|
775
889
|
maxToolsPerServer?: number | undefined;
|
|
@@ -1380,7 +1494,7 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
1380
1494
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1381
1495
|
/** Named MCP servers, keyed by a short server id used as the tool prefix
|
|
1382
1496
|
* (`mcp__<server>__<tool>`) and in the trust prompt. */
|
|
1383
|
-
servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
|
|
1497
|
+
servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodObject<{
|
|
1384
1498
|
/** stdio transport: the server program to spawn. */
|
|
1385
1499
|
command: z.ZodOptional<z.ZodString>;
|
|
1386
1500
|
/** Arguments for `command`. */
|
|
@@ -1389,26 +1503,78 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
1389
1503
|
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
1390
1504
|
/** Remote transport: the server URL (mutually exclusive with `command`). */
|
|
1391
1505
|
url: z.ZodOptional<z.ZodString>;
|
|
1506
|
+
/**
|
|
1507
|
+
* Remote auth (C.27c, url only): the NAME of a bearer credential, resolved
|
|
1508
|
+
* solely from `~/.cruxy/credentials.json` (0600) as `Authorization: Bearer …`.
|
|
1509
|
+
* This field holds a NAME, never the secret — a project config may name a
|
|
1510
|
+
* credential it does not contain, so the value never lives in-repo. Resolution
|
|
1511
|
+
* never consults the environment or any config file. Requires an `https` URL.
|
|
1512
|
+
*/
|
|
1513
|
+
credentialRef: z.ZodOptional<z.ZodString>;
|
|
1514
|
+
/**
|
|
1515
|
+
* Remote auth (C.27c, url only): raw request headers sent to the endpoint.
|
|
1516
|
+
* A LIVE header value is a secret, so this is accepted ONLY from user-scope
|
|
1517
|
+
* config (`~/.cruxy/config.json`) — the loader REJECTS it from project-scope
|
|
1518
|
+
* config (a cloned repo must not inject live headers). Prefer `credentialRef`.
|
|
1519
|
+
* Requires an `https` URL; reserved framing headers cannot be overridden.
|
|
1520
|
+
*/
|
|
1521
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
1392
1522
|
}, "strict", z.ZodTypeAny, {
|
|
1393
1523
|
args: string[];
|
|
1394
1524
|
env: Record<string, string>;
|
|
1395
1525
|
command?: string | undefined;
|
|
1526
|
+
credentialRef?: string | undefined;
|
|
1527
|
+
url?: string | undefined;
|
|
1528
|
+
headers?: Record<string, string> | undefined;
|
|
1529
|
+
}, {
|
|
1530
|
+
command?: string | undefined;
|
|
1531
|
+
credentialRef?: string | undefined;
|
|
1532
|
+
url?: string | undefined;
|
|
1533
|
+
args?: string[] | undefined;
|
|
1534
|
+
env?: Record<string, string> | undefined;
|
|
1535
|
+
headers?: Record<string, string> | undefined;
|
|
1536
|
+
}>, {
|
|
1537
|
+
args: string[];
|
|
1538
|
+
env: Record<string, string>;
|
|
1539
|
+
command?: string | undefined;
|
|
1540
|
+
credentialRef?: string | undefined;
|
|
1541
|
+
url?: string | undefined;
|
|
1542
|
+
headers?: Record<string, string> | undefined;
|
|
1543
|
+
}, {
|
|
1544
|
+
command?: string | undefined;
|
|
1545
|
+
credentialRef?: string | undefined;
|
|
1546
|
+
url?: string | undefined;
|
|
1547
|
+
args?: string[] | undefined;
|
|
1548
|
+
env?: Record<string, string> | undefined;
|
|
1549
|
+
headers?: Record<string, string> | undefined;
|
|
1550
|
+
}>, {
|
|
1551
|
+
args: string[];
|
|
1552
|
+
env: Record<string, string>;
|
|
1553
|
+
command?: string | undefined;
|
|
1554
|
+
credentialRef?: string | undefined;
|
|
1396
1555
|
url?: string | undefined;
|
|
1556
|
+
headers?: Record<string, string> | undefined;
|
|
1397
1557
|
}, {
|
|
1398
1558
|
command?: string | undefined;
|
|
1559
|
+
credentialRef?: string | undefined;
|
|
1399
1560
|
url?: string | undefined;
|
|
1400
1561
|
args?: string[] | undefined;
|
|
1401
1562
|
env?: Record<string, string> | undefined;
|
|
1563
|
+
headers?: Record<string, string> | undefined;
|
|
1402
1564
|
}>, {
|
|
1403
1565
|
args: string[];
|
|
1404
1566
|
env: Record<string, string>;
|
|
1405
1567
|
command?: string | undefined;
|
|
1568
|
+
credentialRef?: string | undefined;
|
|
1406
1569
|
url?: string | undefined;
|
|
1570
|
+
headers?: Record<string, string> | undefined;
|
|
1407
1571
|
}, {
|
|
1408
1572
|
command?: string | undefined;
|
|
1573
|
+
credentialRef?: string | undefined;
|
|
1409
1574
|
url?: string | undefined;
|
|
1410
1575
|
args?: string[] | undefined;
|
|
1411
1576
|
env?: Record<string, string> | undefined;
|
|
1577
|
+
headers?: Record<string, string> | undefined;
|
|
1412
1578
|
}>>>;
|
|
1413
1579
|
/** Fail a server's `initialize` handshake (its tools are skipped) if it does
|
|
1414
1580
|
* not complete within this many ms. */
|
|
@@ -1432,7 +1598,9 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
1432
1598
|
args: string[];
|
|
1433
1599
|
env: Record<string, string>;
|
|
1434
1600
|
command?: string | undefined;
|
|
1601
|
+
credentialRef?: string | undefined;
|
|
1435
1602
|
url?: string | undefined;
|
|
1603
|
+
headers?: Record<string, string> | undefined;
|
|
1436
1604
|
}>;
|
|
1437
1605
|
enabled: boolean;
|
|
1438
1606
|
maxToolsPerServer: number;
|
|
@@ -1443,9 +1611,11 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
1443
1611
|
requestTimeout?: number | undefined;
|
|
1444
1612
|
servers?: Record<string, {
|
|
1445
1613
|
command?: string | undefined;
|
|
1614
|
+
credentialRef?: string | undefined;
|
|
1446
1615
|
url?: string | undefined;
|
|
1447
1616
|
args?: string[] | undefined;
|
|
1448
1617
|
env?: Record<string, string> | undefined;
|
|
1618
|
+
headers?: Record<string, string> | undefined;
|
|
1449
1619
|
}> | undefined;
|
|
1450
1620
|
enabled?: boolean | undefined;
|
|
1451
1621
|
maxToolsPerServer?: number | undefined;
|
|
@@ -1633,7 +1803,9 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
1633
1803
|
args: string[];
|
|
1634
1804
|
env: Record<string, string>;
|
|
1635
1805
|
command?: string | undefined;
|
|
1806
|
+
credentialRef?: string | undefined;
|
|
1636
1807
|
url?: string | undefined;
|
|
1808
|
+
headers?: Record<string, string> | undefined;
|
|
1637
1809
|
}>;
|
|
1638
1810
|
enabled: boolean;
|
|
1639
1811
|
maxToolsPerServer: number;
|
|
@@ -1781,9 +1953,11 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
1781
1953
|
requestTimeout?: number | undefined;
|
|
1782
1954
|
servers?: Record<string, {
|
|
1783
1955
|
command?: string | undefined;
|
|
1956
|
+
credentialRef?: string | undefined;
|
|
1784
1957
|
url?: string | undefined;
|
|
1785
1958
|
args?: string[] | undefined;
|
|
1786
1959
|
env?: Record<string, string> | undefined;
|
|
1960
|
+
headers?: Record<string, string> | undefined;
|
|
1787
1961
|
}> | undefined;
|
|
1788
1962
|
enabled?: boolean | undefined;
|
|
1789
1963
|
maxToolsPerServer?: number | undefined;
|