@cruxy/cli 0.20.0 → 0.21.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/approval/classify.js +24 -0
- package/dist/approval/policy.js +7 -0
- package/dist/approval/prompt.js +7 -0
- package/dist/approval/types.d.ts +6 -0
- package/dist/brand/voice.d.ts +1 -1
- package/dist/brand/voice.js +1 -1
- package/dist/cli/commands/mcp.d.ts +9 -0
- package/dist/cli/commands/mcp.js +87 -0
- package/dist/cli/commands/run.js +22 -5
- package/dist/cli/program.js +2 -0
- package/dist/cli/session-factory.d.ts +2 -2
- package/dist/cli/session-factory.js +9 -2
- package/dist/config/schema.d.ts +228 -30
- package/dist/config/schema.js +55 -4
- package/dist/constants.d.ts +8 -0
- package/dist/constants.js +8 -0
- package/dist/errors/constructors.d.ts +17 -0
- package/dist/errors/constructors.js +46 -0
- package/dist/errors/types.d.ts +9 -0
- package/dist/errors/types.js +15 -0
- package/dist/lsp/transport.d.ts +6 -15
- package/dist/lsp/transport.js +10 -66
- package/dist/mcp/adapter.d.ts +44 -0
- package/dist/mcp/adapter.js +70 -0
- package/dist/mcp/bounds.d.ts +35 -0
- package/dist/mcp/bounds.js +36 -0
- package/dist/mcp/client.d.ts +19 -0
- package/dist/mcp/client.js +93 -0
- package/dist/mcp/demarcate.d.ts +12 -0
- package/dist/mcp/demarcate.js +71 -0
- package/dist/mcp/index.d.ts +9 -0
- package/dist/mcp/index.js +8 -0
- package/dist/mcp/service.d.ts +54 -0
- package/dist/mcp/service.js +99 -0
- package/dist/mcp/transport.d.ts +30 -0
- package/dist/mcp/transport.js +188 -0
- package/dist/mcp/trust-gate.d.ts +35 -0
- package/dist/mcp/trust-gate.js +40 -0
- package/dist/mcp/trust.d.ts +52 -0
- package/dist/mcp/trust.js +111 -0
- package/dist/mcp/types.d.ts +52 -0
- package/dist/mcp/types.js +7 -0
- package/dist/tools/registry.js +3 -1
- package/dist/tools/types.d.ts +15 -1
- package/dist/utils/child-tree.d.ts +35 -0
- package/dist/utils/child-tree.js +76 -0
- package/package.json +1 -1
package/dist/config/schema.d.ts
CHANGED
|
@@ -446,16 +446,16 @@ export declare const LspConfigSchema: z.ZodObject<{
|
|
|
446
446
|
}, "strict", z.ZodTypeAny, {
|
|
447
447
|
startupTimeout: number;
|
|
448
448
|
requestTimeout: number;
|
|
449
|
-
enabled: boolean;
|
|
450
449
|
servers: Record<string, string>;
|
|
450
|
+
enabled: boolean;
|
|
451
451
|
maxServers: number;
|
|
452
452
|
idleTimeout: number;
|
|
453
453
|
maxResults: number;
|
|
454
454
|
}, {
|
|
455
455
|
startupTimeout?: number | undefined;
|
|
456
456
|
requestTimeout?: number | undefined;
|
|
457
|
-
enabled?: boolean | undefined;
|
|
458
457
|
servers?: Record<string, string> | undefined;
|
|
458
|
+
enabled?: boolean | undefined;
|
|
459
459
|
maxServers?: number | undefined;
|
|
460
460
|
idleTimeout?: number | undefined;
|
|
461
461
|
maxResults?: number | undefined;
|
|
@@ -593,20 +593,135 @@ export declare const UsageConfigSchema: z.ZodObject<{
|
|
|
593
593
|
} | undefined;
|
|
594
594
|
}>;
|
|
595
595
|
export type UsageConfig = z.infer<typeof UsageConfigSchema>;
|
|
596
|
-
/**
|
|
597
|
-
|
|
596
|
+
/**
|
|
597
|
+
* One MCP server entry (C.27). A `command` (+ optional `args`/`env`) is a stdio
|
|
598
|
+
* server cruxy spawns as a child process; a `url` names a remote server. Exactly
|
|
599
|
+
* one transport must be given. Trusting a stdio server runs its code UNSANDBOXED
|
|
600
|
+
* with your privileges, which is why connection is gated by an explicit,
|
|
601
|
+
* fingerprinted trust decision (see `mcp/trust.ts`).
|
|
602
|
+
*/
|
|
603
|
+
export declare const McpServerSchema: z.ZodEffects<z.ZodObject<{
|
|
604
|
+
/** stdio transport: the server program to spawn. */
|
|
598
605
|
command: z.ZodOptional<z.ZodString>;
|
|
599
|
-
|
|
606
|
+
/** Arguments for `command`. */
|
|
607
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
608
|
+
/** Extra environment variables for the spawned server (stdio only). */
|
|
609
|
+
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
610
|
+
/** Remote transport: the server URL (mutually exclusive with `command`). */
|
|
600
611
|
url: z.ZodOptional<z.ZodString>;
|
|
601
612
|
}, "strict", z.ZodTypeAny, {
|
|
613
|
+
args: string[];
|
|
614
|
+
env: Record<string, string>;
|
|
615
|
+
command?: string | undefined;
|
|
616
|
+
url?: string | undefined;
|
|
617
|
+
}, {
|
|
602
618
|
command?: string | undefined;
|
|
603
619
|
url?: string | undefined;
|
|
604
620
|
args?: string[] | undefined;
|
|
621
|
+
env?: Record<string, string> | undefined;
|
|
622
|
+
}>, {
|
|
623
|
+
args: string[];
|
|
624
|
+
env: Record<string, string>;
|
|
625
|
+
command?: string | undefined;
|
|
626
|
+
url?: string | undefined;
|
|
605
627
|
}, {
|
|
606
628
|
command?: string | undefined;
|
|
607
629
|
url?: string | undefined;
|
|
608
630
|
args?: string[] | undefined;
|
|
631
|
+
env?: Record<string, string> | undefined;
|
|
632
|
+
}>;
|
|
633
|
+
export type McpServerConfig = z.infer<typeof McpServerSchema>;
|
|
634
|
+
/**
|
|
635
|
+
* MCP client integration (C.27): connect to trusted MCP servers and expose their
|
|
636
|
+
* tools to the agent. OFF by default — like the sandbox, LSP, and hooks, it runs
|
|
637
|
+
* EXTERNAL code (a stdio server executes UNSANDBOXED with your privileges), a
|
|
638
|
+
* real execution surface you opt into explicitly. When off, nothing connects or
|
|
639
|
+
* spawns and no MCP tool is ever registered. Every server-advertised tool is
|
|
640
|
+
* gated (destructive tier — a server can never self-declare a tool "safe"), its
|
|
641
|
+
* description and results are demarcated as untrusted external data with upstream
|
|
642
|
+
* model names scrubbed, and results are NEVER persisted. The tool list a server
|
|
643
|
+
* advertises is bounded (count + per-tool description/schema size) so a hostile
|
|
644
|
+
* server can't blow the context budget.
|
|
645
|
+
*/
|
|
646
|
+
export declare const McpConfigSchema: z.ZodObject<{
|
|
647
|
+
/** Master switch. When false, no server is connected/spawned and no MCP tool
|
|
648
|
+
* is registered (the feature stays fully inert). */
|
|
649
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
650
|
+
/** Named MCP servers, keyed by a short server id used as the tool prefix
|
|
651
|
+
* (`mcp__<server>__<tool>`) and in the trust prompt. */
|
|
652
|
+
servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
|
|
653
|
+
/** stdio transport: the server program to spawn. */
|
|
654
|
+
command: z.ZodOptional<z.ZodString>;
|
|
655
|
+
/** Arguments for `command`. */
|
|
656
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
657
|
+
/** Extra environment variables for the spawned server (stdio only). */
|
|
658
|
+
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
659
|
+
/** Remote transport: the server URL (mutually exclusive with `command`). */
|
|
660
|
+
url: z.ZodOptional<z.ZodString>;
|
|
661
|
+
}, "strict", z.ZodTypeAny, {
|
|
662
|
+
args: string[];
|
|
663
|
+
env: Record<string, string>;
|
|
664
|
+
command?: string | undefined;
|
|
665
|
+
url?: string | undefined;
|
|
666
|
+
}, {
|
|
667
|
+
command?: string | undefined;
|
|
668
|
+
url?: string | undefined;
|
|
669
|
+
args?: string[] | undefined;
|
|
670
|
+
env?: Record<string, string> | undefined;
|
|
671
|
+
}>, {
|
|
672
|
+
args: string[];
|
|
673
|
+
env: Record<string, string>;
|
|
674
|
+
command?: string | undefined;
|
|
675
|
+
url?: string | undefined;
|
|
676
|
+
}, {
|
|
677
|
+
command?: string | undefined;
|
|
678
|
+
url?: string | undefined;
|
|
679
|
+
args?: string[] | undefined;
|
|
680
|
+
env?: Record<string, string> | undefined;
|
|
681
|
+
}>>>;
|
|
682
|
+
/** Fail a server's `initialize` handshake (its tools are skipped) if it does
|
|
683
|
+
* not complete within this many ms. */
|
|
684
|
+
startupTimeout: z.ZodDefault<z.ZodNumber>;
|
|
685
|
+
/** Fail a single `tools/call` if the server does not respond within this many
|
|
686
|
+
* ms — the connection is kept, only the one call errors. */
|
|
687
|
+
requestTimeout: z.ZodDefault<z.ZodNumber>;
|
|
688
|
+
/** Max tools accepted from ONE server; extras are dropped with a visible note
|
|
689
|
+
* (a hostile server can't advertise thousands of tools to flood context). */
|
|
690
|
+
maxToolsPerServer: z.ZodDefault<z.ZodNumber>;
|
|
691
|
+
/** Max characters kept from a single tool's description; the rest is truncated
|
|
692
|
+
* with a visible marker. */
|
|
693
|
+
maxDescriptionChars: z.ZodDefault<z.ZodNumber>;
|
|
694
|
+
/** Max bytes kept from a single tool's advertised JSON input schema; an
|
|
695
|
+
* over-cap schema is replaced with a permissive one and a visible note. */
|
|
696
|
+
maxSchemaBytes: z.ZodDefault<z.ZodNumber>;
|
|
697
|
+
}, "strict", z.ZodTypeAny, {
|
|
698
|
+
startupTimeout: number;
|
|
699
|
+
requestTimeout: number;
|
|
700
|
+
servers: Record<string, {
|
|
701
|
+
args: string[];
|
|
702
|
+
env: Record<string, string>;
|
|
703
|
+
command?: string | undefined;
|
|
704
|
+
url?: string | undefined;
|
|
705
|
+
}>;
|
|
706
|
+
enabled: boolean;
|
|
707
|
+
maxToolsPerServer: number;
|
|
708
|
+
maxDescriptionChars: number;
|
|
709
|
+
maxSchemaBytes: number;
|
|
710
|
+
}, {
|
|
711
|
+
startupTimeout?: number | undefined;
|
|
712
|
+
requestTimeout?: number | undefined;
|
|
713
|
+
servers?: Record<string, {
|
|
714
|
+
command?: string | undefined;
|
|
715
|
+
url?: string | undefined;
|
|
716
|
+
args?: string[] | undefined;
|
|
717
|
+
env?: Record<string, string> | undefined;
|
|
718
|
+
}> | undefined;
|
|
719
|
+
enabled?: boolean | undefined;
|
|
720
|
+
maxToolsPerServer?: number | undefined;
|
|
721
|
+
maxDescriptionChars?: number | undefined;
|
|
722
|
+
maxSchemaBytes?: number | undefined;
|
|
609
723
|
}>;
|
|
724
|
+
export type McpConfig = z.infer<typeof McpConfigSchema>;
|
|
610
725
|
export declare const CruxyConfigSchema: z.ZodObject<{
|
|
611
726
|
model: z.ZodDefault<z.ZodObject<{
|
|
612
727
|
provider: z.ZodDefault<z.ZodEnum<["cruxy", "anthropic", "openai", "custom"]>>;
|
|
@@ -823,16 +938,16 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
823
938
|
}, "strict", z.ZodTypeAny, {
|
|
824
939
|
startupTimeout: number;
|
|
825
940
|
requestTimeout: number;
|
|
826
|
-
enabled: boolean;
|
|
827
941
|
servers: Record<string, string>;
|
|
942
|
+
enabled: boolean;
|
|
828
943
|
maxServers: number;
|
|
829
944
|
idleTimeout: number;
|
|
830
945
|
maxResults: number;
|
|
831
946
|
}, {
|
|
832
947
|
startupTimeout?: number | undefined;
|
|
833
948
|
requestTimeout?: number | undefined;
|
|
834
|
-
enabled?: boolean | undefined;
|
|
835
949
|
servers?: Record<string, string> | undefined;
|
|
950
|
+
enabled?: boolean | undefined;
|
|
836
951
|
maxServers?: number | undefined;
|
|
837
952
|
idleTimeout?: number | undefined;
|
|
838
953
|
maxResults?: number | undefined;
|
|
@@ -1104,19 +1219,84 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
1104
1219
|
} | undefined;
|
|
1105
1220
|
} | undefined;
|
|
1106
1221
|
}>>;
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1222
|
+
mcp: z.ZodDefault<z.ZodObject<{
|
|
1223
|
+
/** Master switch. When false, no server is connected/spawned and no MCP tool
|
|
1224
|
+
* is registered (the feature stays fully inert). */
|
|
1225
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1226
|
+
/** Named MCP servers, keyed by a short server id used as the tool prefix
|
|
1227
|
+
* (`mcp__<server>__<tool>`) and in the trust prompt. */
|
|
1228
|
+
servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
|
|
1229
|
+
/** stdio transport: the server program to spawn. */
|
|
1230
|
+
command: z.ZodOptional<z.ZodString>;
|
|
1231
|
+
/** Arguments for `command`. */
|
|
1232
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
1233
|
+
/** Extra environment variables for the spawned server (stdio only). */
|
|
1234
|
+
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
1235
|
+
/** Remote transport: the server URL (mutually exclusive with `command`). */
|
|
1236
|
+
url: z.ZodOptional<z.ZodString>;
|
|
1237
|
+
}, "strict", z.ZodTypeAny, {
|
|
1238
|
+
args: string[];
|
|
1239
|
+
env: Record<string, string>;
|
|
1240
|
+
command?: string | undefined;
|
|
1241
|
+
url?: string | undefined;
|
|
1242
|
+
}, {
|
|
1243
|
+
command?: string | undefined;
|
|
1244
|
+
url?: string | undefined;
|
|
1245
|
+
args?: string[] | undefined;
|
|
1246
|
+
env?: Record<string, string> | undefined;
|
|
1247
|
+
}>, {
|
|
1248
|
+
args: string[];
|
|
1249
|
+
env: Record<string, string>;
|
|
1250
|
+
command?: string | undefined;
|
|
1251
|
+
url?: string | undefined;
|
|
1252
|
+
}, {
|
|
1253
|
+
command?: string | undefined;
|
|
1254
|
+
url?: string | undefined;
|
|
1255
|
+
args?: string[] | undefined;
|
|
1256
|
+
env?: Record<string, string> | undefined;
|
|
1257
|
+
}>>>;
|
|
1258
|
+
/** Fail a server's `initialize` handshake (its tools are skipped) if it does
|
|
1259
|
+
* not complete within this many ms. */
|
|
1260
|
+
startupTimeout: z.ZodDefault<z.ZodNumber>;
|
|
1261
|
+
/** Fail a single `tools/call` if the server does not respond within this many
|
|
1262
|
+
* ms — the connection is kept, only the one call errors. */
|
|
1263
|
+
requestTimeout: z.ZodDefault<z.ZodNumber>;
|
|
1264
|
+
/** Max tools accepted from ONE server; extras are dropped with a visible note
|
|
1265
|
+
* (a hostile server can't advertise thousands of tools to flood context). */
|
|
1266
|
+
maxToolsPerServer: z.ZodDefault<z.ZodNumber>;
|
|
1267
|
+
/** Max characters kept from a single tool's description; the rest is truncated
|
|
1268
|
+
* with a visible marker. */
|
|
1269
|
+
maxDescriptionChars: z.ZodDefault<z.ZodNumber>;
|
|
1270
|
+
/** Max bytes kept from a single tool's advertised JSON input schema; an
|
|
1271
|
+
* over-cap schema is replaced with a permissive one and a visible note. */
|
|
1272
|
+
maxSchemaBytes: z.ZodDefault<z.ZodNumber>;
|
|
1111
1273
|
}, "strict", z.ZodTypeAny, {
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1274
|
+
startupTimeout: number;
|
|
1275
|
+
requestTimeout: number;
|
|
1276
|
+
servers: Record<string, {
|
|
1277
|
+
args: string[];
|
|
1278
|
+
env: Record<string, string>;
|
|
1279
|
+
command?: string | undefined;
|
|
1280
|
+
url?: string | undefined;
|
|
1281
|
+
}>;
|
|
1282
|
+
enabled: boolean;
|
|
1283
|
+
maxToolsPerServer: number;
|
|
1284
|
+
maxDescriptionChars: number;
|
|
1285
|
+
maxSchemaBytes: number;
|
|
1115
1286
|
}, {
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1287
|
+
startupTimeout?: number | undefined;
|
|
1288
|
+
requestTimeout?: number | undefined;
|
|
1289
|
+
servers?: Record<string, {
|
|
1290
|
+
command?: string | undefined;
|
|
1291
|
+
url?: string | undefined;
|
|
1292
|
+
args?: string[] | undefined;
|
|
1293
|
+
env?: Record<string, string> | undefined;
|
|
1294
|
+
}> | undefined;
|
|
1295
|
+
enabled?: boolean | undefined;
|
|
1296
|
+
maxToolsPerServer?: number | undefined;
|
|
1297
|
+
maxDescriptionChars?: number | undefined;
|
|
1298
|
+
maxSchemaBytes?: number | undefined;
|
|
1299
|
+
}>>;
|
|
1120
1300
|
logLevel: z.ZodDefault<z.ZodEnum<["debug", "info", "warn", "error", "silent"]>>;
|
|
1121
1301
|
}, "strict", z.ZodTypeAny, {
|
|
1122
1302
|
cruxy: {
|
|
@@ -1218,8 +1398,8 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
1218
1398
|
lsp: {
|
|
1219
1399
|
startupTimeout: number;
|
|
1220
1400
|
requestTimeout: number;
|
|
1221
|
-
enabled: boolean;
|
|
1222
1401
|
servers: Record<string, string>;
|
|
1402
|
+
enabled: boolean;
|
|
1223
1403
|
maxServers: number;
|
|
1224
1404
|
idleTimeout: number;
|
|
1225
1405
|
maxResults: number;
|
|
@@ -1237,11 +1417,20 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
1237
1417
|
map: Partial<Record<"main-turn" | "subagent" | "plan" | "commit-msg" | "classify" | "summarize", "kavi" | "vaani" | "mira">>;
|
|
1238
1418
|
default?: "kavi" | "vaani" | "mira" | undefined;
|
|
1239
1419
|
};
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1420
|
+
mcp: {
|
|
1421
|
+
startupTimeout: number;
|
|
1422
|
+
requestTimeout: number;
|
|
1423
|
+
servers: Record<string, {
|
|
1424
|
+
args: string[];
|
|
1425
|
+
env: Record<string, string>;
|
|
1426
|
+
command?: string | undefined;
|
|
1427
|
+
url?: string | undefined;
|
|
1428
|
+
}>;
|
|
1429
|
+
enabled: boolean;
|
|
1430
|
+
maxToolsPerServer: number;
|
|
1431
|
+
maxDescriptionChars: number;
|
|
1432
|
+
maxSchemaBytes: number;
|
|
1433
|
+
};
|
|
1245
1434
|
logLevel: "debug" | "info" | "warn" | "error" | "silent";
|
|
1246
1435
|
}, {
|
|
1247
1436
|
cruxy?: {
|
|
@@ -1343,8 +1532,8 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
1343
1532
|
lsp?: {
|
|
1344
1533
|
startupTimeout?: number | undefined;
|
|
1345
1534
|
requestTimeout?: number | undefined;
|
|
1346
|
-
enabled?: boolean | undefined;
|
|
1347
1535
|
servers?: Record<string, string> | undefined;
|
|
1536
|
+
enabled?: boolean | undefined;
|
|
1348
1537
|
maxServers?: number | undefined;
|
|
1349
1538
|
idleTimeout?: number | undefined;
|
|
1350
1539
|
maxResults?: number | undefined;
|
|
@@ -1362,11 +1551,20 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
1362
1551
|
map?: Partial<Record<"main-turn" | "subagent" | "plan" | "commit-msg" | "classify" | "summarize", "kavi" | "vaani" | "mira">> | undefined;
|
|
1363
1552
|
default?: "kavi" | "vaani" | "mira" | undefined;
|
|
1364
1553
|
} | undefined;
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1554
|
+
mcp?: {
|
|
1555
|
+
startupTimeout?: number | undefined;
|
|
1556
|
+
requestTimeout?: number | undefined;
|
|
1557
|
+
servers?: Record<string, {
|
|
1558
|
+
command?: string | undefined;
|
|
1559
|
+
url?: string | undefined;
|
|
1560
|
+
args?: string[] | undefined;
|
|
1561
|
+
env?: Record<string, string> | undefined;
|
|
1562
|
+
}> | undefined;
|
|
1563
|
+
enabled?: boolean | undefined;
|
|
1564
|
+
maxToolsPerServer?: number | undefined;
|
|
1565
|
+
maxDescriptionChars?: number | undefined;
|
|
1566
|
+
maxSchemaBytes?: number | undefined;
|
|
1567
|
+
} | undefined;
|
|
1370
1568
|
logLevel?: "debug" | "info" | "warn" | "error" | "silent" | undefined;
|
|
1371
1569
|
}>;
|
|
1372
1570
|
export type CruxyConfig = z.infer<typeof CruxyConfigSchema>;
|
package/dist/config/schema.js
CHANGED
|
@@ -365,12 +365,63 @@ export const UsageConfigSchema = z
|
|
|
365
365
|
.default({}),
|
|
366
366
|
})
|
|
367
367
|
.strict();
|
|
368
|
-
/**
|
|
368
|
+
/**
|
|
369
|
+
* One MCP server entry (C.27). A `command` (+ optional `args`/`env`) is a stdio
|
|
370
|
+
* server cruxy spawns as a child process; a `url` names a remote server. Exactly
|
|
371
|
+
* one transport must be given. Trusting a stdio server runs its code UNSANDBOXED
|
|
372
|
+
* with your privileges, which is why connection is gated by an explicit,
|
|
373
|
+
* fingerprinted trust decision (see `mcp/trust.ts`).
|
|
374
|
+
*/
|
|
369
375
|
export const McpServerSchema = z
|
|
370
376
|
.object({
|
|
371
|
-
|
|
372
|
-
|
|
377
|
+
/** stdio transport: the server program to spawn. */
|
|
378
|
+
command: z.string().min(1).optional(),
|
|
379
|
+
/** Arguments for `command`. */
|
|
380
|
+
args: z.array(z.string()).default([]),
|
|
381
|
+
/** Extra environment variables for the spawned server (stdio only). */
|
|
382
|
+
env: z.record(z.string(), z.string()).default({}),
|
|
383
|
+
/** Remote transport: the server URL (mutually exclusive with `command`). */
|
|
373
384
|
url: z.string().url().optional(),
|
|
385
|
+
})
|
|
386
|
+
.strict()
|
|
387
|
+
.refine((s) => Boolean(s.command) !== Boolean(s.url), {
|
|
388
|
+
message: "an MCP server needs exactly one of `command` (stdio) or `url`",
|
|
389
|
+
});
|
|
390
|
+
/**
|
|
391
|
+
* MCP client integration (C.27): connect to trusted MCP servers and expose their
|
|
392
|
+
* tools to the agent. OFF by default — like the sandbox, LSP, and hooks, it runs
|
|
393
|
+
* EXTERNAL code (a stdio server executes UNSANDBOXED with your privileges), a
|
|
394
|
+
* real execution surface you opt into explicitly. When off, nothing connects or
|
|
395
|
+
* spawns and no MCP tool is ever registered. Every server-advertised tool is
|
|
396
|
+
* gated (destructive tier — a server can never self-declare a tool "safe"), its
|
|
397
|
+
* description and results are demarcated as untrusted external data with upstream
|
|
398
|
+
* model names scrubbed, and results are NEVER persisted. The tool list a server
|
|
399
|
+
* advertises is bounded (count + per-tool description/schema size) so a hostile
|
|
400
|
+
* server can't blow the context budget.
|
|
401
|
+
*/
|
|
402
|
+
export const McpConfigSchema = z
|
|
403
|
+
.object({
|
|
404
|
+
/** Master switch. When false, no server is connected/spawned and no MCP tool
|
|
405
|
+
* is registered (the feature stays fully inert). */
|
|
406
|
+
enabled: z.boolean().default(false),
|
|
407
|
+
/** Named MCP servers, keyed by a short server id used as the tool prefix
|
|
408
|
+
* (`mcp__<server>__<tool>`) and in the trust prompt. */
|
|
409
|
+
servers: z.record(z.string(), McpServerSchema).default({}),
|
|
410
|
+
/** Fail a server's `initialize` handshake (its tools are skipped) if it does
|
|
411
|
+
* not complete within this many ms. */
|
|
412
|
+
startupTimeout: z.number().int().positive().default(15000),
|
|
413
|
+
/** Fail a single `tools/call` if the server does not respond within this many
|
|
414
|
+
* ms — the connection is kept, only the one call errors. */
|
|
415
|
+
requestTimeout: z.number().int().positive().default(30000),
|
|
416
|
+
/** Max tools accepted from ONE server; extras are dropped with a visible note
|
|
417
|
+
* (a hostile server can't advertise thousands of tools to flood context). */
|
|
418
|
+
maxToolsPerServer: z.number().int().positive().default(32),
|
|
419
|
+
/** Max characters kept from a single tool's description; the rest is truncated
|
|
420
|
+
* with a visible marker. */
|
|
421
|
+
maxDescriptionChars: z.number().int().positive().default(1024),
|
|
422
|
+
/** Max bytes kept from a single tool's advertised JSON input schema; an
|
|
423
|
+
* over-cap schema is replaced with a permissive one and a visible note. */
|
|
424
|
+
maxSchemaBytes: z.number().int().positive().default(8192),
|
|
374
425
|
})
|
|
375
426
|
.strict();
|
|
376
427
|
export const CruxyConfigSchema = z
|
|
@@ -393,7 +444,7 @@ export const CruxyConfigSchema = z
|
|
|
393
444
|
routing: RoutingConfigSchema.default({}),
|
|
394
445
|
memory: MemoryConfigSchema.default({}),
|
|
395
446
|
usage: UsageConfigSchema.default({}),
|
|
396
|
-
|
|
447
|
+
mcp: McpConfigSchema.default({}),
|
|
397
448
|
logLevel: z.enum(LOG_LEVELS).default("info"),
|
|
398
449
|
})
|
|
399
450
|
.strict();
|
package/dist/constants.d.ts
CHANGED
|
@@ -41,6 +41,14 @@ export declare const MEMORY_FILE_NAME = "entries.json";
|
|
|
41
41
|
* (`~/.cruxy/memory-trust.json`) — its own file, independent of hook trust, so
|
|
42
42
|
* cloning a repo carries zero memory trust (C.29 supply-chain safety). */
|
|
43
43
|
export declare const MEMORY_TRUST_FILE_NAME = "memory-trust.json";
|
|
44
|
+
/**
|
|
45
|
+
* MCP server integration (C.27). Per-repo trust for configured MCP servers, in
|
|
46
|
+
* the GLOBAL dir only (`~/.cruxy/mcp-trust.json`) — its own file, independent of
|
|
47
|
+
* hook and memory trust, so cloning a repo carries zero MCP trust. Trusting a
|
|
48
|
+
* server runs its code UNSANDBOXED with your privileges, so this decision is
|
|
49
|
+
* fingerprinted and re-checked on every run (C.27 supply-chain safety).
|
|
50
|
+
*/
|
|
51
|
+
export declare const MCP_TRUST_FILE_NAME = "mcp-trust.json";
|
|
44
52
|
/**
|
|
45
53
|
* Usage telemetry + cost tracking (C.22). Per-run/per-session usage records live
|
|
46
54
|
* in the GLOBAL dir only (`~/.cruxy/usage/runs.json`), `0600` — LOCAL accounting
|
package/dist/constants.js
CHANGED
|
@@ -61,6 +61,14 @@ export const MEMORY_FILE_NAME = "entries.json";
|
|
|
61
61
|
* (`~/.cruxy/memory-trust.json`) — its own file, independent of hook trust, so
|
|
62
62
|
* cloning a repo carries zero memory trust (C.29 supply-chain safety). */
|
|
63
63
|
export const MEMORY_TRUST_FILE_NAME = "memory-trust.json";
|
|
64
|
+
/**
|
|
65
|
+
* MCP server integration (C.27). Per-repo trust for configured MCP servers, in
|
|
66
|
+
* the GLOBAL dir only (`~/.cruxy/mcp-trust.json`) — its own file, independent of
|
|
67
|
+
* hook and memory trust, so cloning a repo carries zero MCP trust. Trusting a
|
|
68
|
+
* server runs its code UNSANDBOXED with your privileges, so this decision is
|
|
69
|
+
* fingerprinted and re-checked on every run (C.27 supply-chain safety).
|
|
70
|
+
*/
|
|
71
|
+
export const MCP_TRUST_FILE_NAME = "mcp-trust.json";
|
|
64
72
|
/**
|
|
65
73
|
* Usage telemetry + cost tracking (C.22). Per-run/per-session usage records live
|
|
66
74
|
* in the GLOBAL dir only (`~/.cruxy/usage/runs.json`), `0600` — LOCAL accounting
|
|
@@ -180,6 +180,23 @@ export declare function lspTimeout(language: string, phase: "startup" | "request
|
|
|
180
180
|
* that answered with zero results (that is an ordinary, non-error outcome).
|
|
181
181
|
*/
|
|
182
182
|
export declare function lspCrashed(language: string, detail?: string): CruxyError;
|
|
183
|
+
/**
|
|
184
|
+
* A project configures MCP servers that this repo has not trusted, and cruxy is
|
|
185
|
+
* running non-interactively so it cannot ask. Fail closed BEFORE any server is
|
|
186
|
+
* spawned: trusting a server runs its code UNSANDBOXED with the user's full
|
|
187
|
+
* privileges (a stdio server's own side effects can't be contained by the shell
|
|
188
|
+
* sandbox), so an untrusted config must never connect silently. The wording says
|
|
189
|
+
* this plainly — it is a real escalation, not a "gated shell in a box".
|
|
190
|
+
*/
|
|
191
|
+
export declare function mcpUntrusted(root: string, servers: string[]): CruxyError;
|
|
192
|
+
/**
|
|
193
|
+
* A trusted MCP server could not be reached — spawn failed, the `initialize`
|
|
194
|
+
* handshake errored/timed out, or `tools/list` failed. That server simply
|
|
195
|
+
* contributes no tools (the run continues); the coded reason is surfaced so a
|
|
196
|
+
* misconfigured server is visible, not a silent absence. External server text is
|
|
197
|
+
* gag-scrubbed (U.8) before it reaches the user-facing cause.
|
|
198
|
+
*/
|
|
199
|
+
export declare function mcpConnect(server: string, underlying?: unknown): CruxyError;
|
|
183
200
|
export declare function internal(underlying?: unknown): CruxyError;
|
|
184
201
|
/**
|
|
185
202
|
* Map a known provider/transport error (from `@cruxy/sdk`) to a typed
|
|
@@ -733,6 +733,52 @@ export function lspCrashed(language, detail) {
|
|
|
733
733
|
meta: { language },
|
|
734
734
|
});
|
|
735
735
|
}
|
|
736
|
+
// ── MCP client (exit 16) — C.27 ───────────────────────────────────────────────
|
|
737
|
+
/**
|
|
738
|
+
* A project configures MCP servers that this repo has not trusted, and cruxy is
|
|
739
|
+
* running non-interactively so it cannot ask. Fail closed BEFORE any server is
|
|
740
|
+
* spawned: trusting a server runs its code UNSANDBOXED with the user's full
|
|
741
|
+
* privileges (a stdio server's own side effects can't be contained by the shell
|
|
742
|
+
* sandbox), so an untrusted config must never connect silently. The wording says
|
|
743
|
+
* this plainly — it is a real escalation, not a "gated shell in a box".
|
|
744
|
+
*/
|
|
745
|
+
export function mcpUntrusted(root, servers) {
|
|
746
|
+
const list = servers.join(", ");
|
|
747
|
+
return new CruxyError({
|
|
748
|
+
code: ErrorCode.McpUntrusted,
|
|
749
|
+
title: "this project's MCP servers have not been trusted",
|
|
750
|
+
cause: `${servers.length} configured server${servers.length === 1 ? "" : "s"} (${list}) ` +
|
|
751
|
+
"would run UNSANDBOXED with your full privileges — cruxy will not connect to them " +
|
|
752
|
+
"non-interactively without an explicit, recorded trust decision",
|
|
753
|
+
nextSteps: [
|
|
754
|
+
"review the servers with `cruxy mcp list`",
|
|
755
|
+
"then trust them with `cruxy mcp trust .` (re-trust is required if the config changes)",
|
|
756
|
+
"or set `mcp.enabled = false` to disable MCP for this project",
|
|
757
|
+
],
|
|
758
|
+
meta: { root, servers },
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* A trusted MCP server could not be reached — spawn failed, the `initialize`
|
|
763
|
+
* handshake errored/timed out, or `tools/list` failed. That server simply
|
|
764
|
+
* contributes no tools (the run continues); the coded reason is surfaced so a
|
|
765
|
+
* misconfigured server is visible, not a silent absence. External server text is
|
|
766
|
+
* gag-scrubbed (U.8) before it reaches the user-facing cause.
|
|
767
|
+
*/
|
|
768
|
+
export function mcpConnect(server, underlying) {
|
|
769
|
+
return new CruxyError({
|
|
770
|
+
code: ErrorCode.McpConnect,
|
|
771
|
+
title: `could not connect to MCP server "${server}"`,
|
|
772
|
+
cause: scrubbedMessageOf(underlying) ??
|
|
773
|
+
"the server failed to start, handshake, or list its tools",
|
|
774
|
+
nextSteps: [
|
|
775
|
+
"re-run with --verbose to see the server's error",
|
|
776
|
+
`verify the command in \`mcp.servers.${server}\` runs standalone`,
|
|
777
|
+
],
|
|
778
|
+
meta: { server },
|
|
779
|
+
underlying,
|
|
780
|
+
});
|
|
781
|
+
}
|
|
736
782
|
// ── internal (exit 1) ─────────────────────────────────────────────────────────
|
|
737
783
|
export function internal(underlying) {
|
|
738
784
|
return new CruxyError({
|
package/dist/errors/types.d.ts
CHANGED
|
@@ -93,6 +93,15 @@ export declare const ErrorCode: {
|
|
|
93
93
|
/** A language server crashed (and, where applicable, a single restart also
|
|
94
94
|
* failed). Distinct from "server returned no results". */
|
|
95
95
|
readonly LspCrashed: "CRUXY_E_LSP_CRASHED";
|
|
96
|
+
/** A project configures MCP servers that have not been trusted for this repo.
|
|
97
|
+
* Trusting a server runs its code UNSANDBOXED with your privileges, so an
|
|
98
|
+
* untrusted config is never connected silently — and in non-interactive mode
|
|
99
|
+
* it fails closed BEFORE any server is spawned (supply-chain safety). */
|
|
100
|
+
readonly McpUntrusted: "CRUXY_E_MCP_UNTRUSTED";
|
|
101
|
+
/** A trusted MCP server failed to spawn, complete the `initialize` handshake,
|
|
102
|
+
* or list its tools. Surfaced (that server contributes no tools) rather than
|
|
103
|
+
* silently swallowed; never fatal to the run. */
|
|
104
|
+
readonly McpConnect: "CRUXY_E_MCP_CONNECT";
|
|
96
105
|
};
|
|
97
106
|
export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
|
|
98
107
|
/** The process exit code for an error code (defaults to 1 for safety). */
|
package/dist/errors/types.js
CHANGED
|
@@ -110,6 +110,16 @@ export const ErrorCode = {
|
|
|
110
110
|
/** A language server crashed (and, where applicable, a single restart also
|
|
111
111
|
* failed). Distinct from "server returned no results". */
|
|
112
112
|
LspCrashed: "CRUXY_E_LSP_CRASHED",
|
|
113
|
+
// MCP client (exit 16) — C.27
|
|
114
|
+
/** A project configures MCP servers that have not been trusted for this repo.
|
|
115
|
+
* Trusting a server runs its code UNSANDBOXED with your privileges, so an
|
|
116
|
+
* untrusted config is never connected silently — and in non-interactive mode
|
|
117
|
+
* it fails closed BEFORE any server is spawned (supply-chain safety). */
|
|
118
|
+
McpUntrusted: "CRUXY_E_MCP_UNTRUSTED",
|
|
119
|
+
/** A trusted MCP server failed to spawn, complete the `initialize` handshake,
|
|
120
|
+
* or list its tools. Surfaced (that server contributes no tools) rather than
|
|
121
|
+
* silently swallowed; never fatal to the run. */
|
|
122
|
+
McpConnect: "CRUXY_E_MCP_CONNECT",
|
|
113
123
|
};
|
|
114
124
|
/**
|
|
115
125
|
* Category exit codes. Distinct per category so a caller (CI, a script) can
|
|
@@ -187,6 +197,11 @@ const EXIT_CODES = {
|
|
|
187
197
|
[ErrorCode.LspServerNotFound]: 15,
|
|
188
198
|
[ErrorCode.LspTimeout]: 15,
|
|
189
199
|
[ErrorCode.LspCrashed]: 15,
|
|
200
|
+
// MCP client (C.27). An untrusted MCP config is an execution-safety stop that
|
|
201
|
+
// fails closed before any spawn; a connect failure surfaces per-server and is
|
|
202
|
+
// never fatal on its own. Grouped for a greppable exit code.
|
|
203
|
+
[ErrorCode.McpUntrusted]: 16,
|
|
204
|
+
[ErrorCode.McpConnect]: 16,
|
|
190
205
|
};
|
|
191
206
|
/** The process exit code for an error code (defaults to 1 for safety). */
|
|
192
207
|
export function exitCodeFor(code) {
|
package/dist/lsp/transport.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
import { killTree, killTrackedTrees, trackedTreeCount } from "../utils/child-tree.js";
|
|
1
2
|
import type { LspTransport, ServerSpec } from "./types.js";
|
|
3
|
+
export { killTree };
|
|
4
|
+
/** @deprecated Use the shared backstop; kept for LSP tests. */
|
|
5
|
+
export declare const killTrackedServers: typeof killTrackedTrees;
|
|
6
|
+
/** @deprecated Use the shared backstop; kept for LSP tests. */
|
|
7
|
+
export declare const trackedServerCount: typeof trackedTreeCount;
|
|
2
8
|
export declare class StdioTransport implements LspTransport {
|
|
3
9
|
private readonly child;
|
|
4
10
|
private nextId;
|
|
@@ -31,18 +37,3 @@ export declare class TransportTimeoutError extends Error {
|
|
|
31
37
|
readonly timeoutMs: number;
|
|
32
38
|
constructor(method: string, timeoutMs: number);
|
|
33
39
|
}
|
|
34
|
-
/**
|
|
35
|
-
* Kill the process's entire group (POSIX negative-PID `SIGKILL`), same helper
|
|
36
|
-
* shape as run_command's `killTree`. Swallows errors — the process may be gone.
|
|
37
|
-
*/
|
|
38
|
-
export declare function killTree(pid: number | undefined): void;
|
|
39
|
-
/**
|
|
40
|
-
* Force-kill the process group of every tracked-but-not-yet-shut-down server,
|
|
41
|
-
* then forget them. This is exactly what the `exit`/`SIGINT`/`SIGTERM`/`SIGHUP`
|
|
42
|
-
* handlers run — the last line against orphaned language servers on a hard exit.
|
|
43
|
-
* Exported so it is directly testable (like `resetIndexServices`) without having
|
|
44
|
-
* to raise real process signals. Idempotent: a second call is a no-op.
|
|
45
|
-
*/
|
|
46
|
-
export declare function killTrackedServers(): void;
|
|
47
|
-
/** Number of servers currently tracked by the exit backstop (for tests). */
|
|
48
|
-
export declare function trackedServerCount(): number;
|