crosscheck-mcp 0.2.23 → 0.2.25

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.
@@ -1,50 +1,9 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
2
  import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
3
3
 
4
- /** A live bridge to a Python crosscheck-agent child. */
5
- interface BridgeHandle {
6
- /** Names of the tools exposed by the Python child (from its
7
- * `tools/list` response). */
8
- readonly toolNames: ReadonlySet<string>;
9
- /** PID of the spawned Python child process, or `null` if the
10
- * transport hasn't started yet / has already closed. Useful for
11
- * shutdown verification and host-side diagnostics. */
12
- readonly pid: number | null;
13
- /** Forward a `tools/call` to the Python child and return the result
14
- * envelope verbatim. */
15
- callTool(name: string, args: Record<string, unknown>): Promise<{
16
- content: {
17
- type: string;
18
- text: string;
19
- }[];
20
- isError?: boolean;
21
- }>;
22
- /** Re-fetch the tool list (in case the child surfaces new tools mid-
23
- * session). Returns the new tool names. */
24
- refreshTools(): Promise<ReadonlySet<string>>;
25
- /** Tear down the bridge cleanly. Idempotent — repeat calls are no-ops. */
26
- close(): Promise<void>;
27
- }
28
-
29
- /** Per-call node-cache config block. Mirrors Python's CFG.node_cache. */
30
- interface NodeCacheCfg {
31
- /** Master enable flag. Defaults to true when the cfg block exists
32
- * in opts; explicitly set false to short-circuit reads + writes. */
33
- enabled?: boolean;
34
- /** Directory to store cache entries. Relative paths are resolved
35
- * against `repoRoot` when supplied; otherwise the CWD. */
36
- dir?: string;
37
- /** Stale-by-mtime threshold (seconds). 0 disables TTL eviction. */
38
- ttl_seconds?: number;
39
- /** Hard cap on the number of JSON entries on disk. 0 disables. */
40
- max_entries?: number;
41
- /** Hard cap on cumulative file size (bytes). 0 disables. */
42
- max_cache_bytes?: number;
43
- }
44
-
45
- interface WorkerToolsCfg {
46
- cost_cap_usd?: unknown;
47
- cost_cap_mode?: unknown;
4
+ interface PricingDoc {
5
+ /** `<provider>` -> `<model>` -> ModelRates. */
6
+ [provider: string]: unknown;
48
7
  }
49
8
 
50
9
  /** sessions row. */
@@ -363,19 +322,6 @@ interface Storage extends StorageRead, StorageWrite {
363
322
  close(): Promise<void>;
364
323
  }
365
324
 
366
- interface BreakerCfg {
367
- max_session_cost_usd?: number;
368
- max_session_tokens?: number;
369
- max_session_wall_seconds?: number;
370
- max_dag_nodes?: number;
371
- max_dag_depth?: number;
372
- }
373
-
374
- interface PricingDoc {
375
- /** `<provider>` -> `<model>` -> ModelRates. */
376
- [provider: string]: unknown;
377
- }
378
-
379
325
  /** One provider call's token + cost record. Mirrors `Usage.to_dict()`
380
326
  * output exactly. */
381
327
  interface Usage {
@@ -451,6 +397,60 @@ interface Provider {
451
397
  send(args: SendArgs): Promise<SendResult>;
452
398
  }
453
399
 
400
+ /** A live bridge to a Python crosscheck-agent child. */
401
+ interface BridgeHandle {
402
+ /** Names of the tools exposed by the Python child (from its
403
+ * `tools/list` response). */
404
+ readonly toolNames: ReadonlySet<string>;
405
+ /** PID of the spawned Python child process, or `null` if the
406
+ * transport hasn't started yet / has already closed. Useful for
407
+ * shutdown verification and host-side diagnostics. */
408
+ readonly pid: number | null;
409
+ /** Forward a `tools/call` to the Python child and return the result
410
+ * envelope verbatim. */
411
+ callTool(name: string, args: Record<string, unknown>): Promise<{
412
+ content: {
413
+ type: string;
414
+ text: string;
415
+ }[];
416
+ isError?: boolean;
417
+ }>;
418
+ /** Re-fetch the tool list (in case the child surfaces new tools mid-
419
+ * session). Returns the new tool names. */
420
+ refreshTools(): Promise<ReadonlySet<string>>;
421
+ /** Tear down the bridge cleanly. Idempotent — repeat calls are no-ops. */
422
+ close(): Promise<void>;
423
+ }
424
+
425
+ /** Per-call node-cache config block. Mirrors Python's CFG.node_cache. */
426
+ interface NodeCacheCfg {
427
+ /** Master enable flag. Defaults to true when the cfg block exists
428
+ * in opts; explicitly set false to short-circuit reads + writes. */
429
+ enabled?: boolean;
430
+ /** Directory to store cache entries. Relative paths are resolved
431
+ * against `repoRoot` when supplied; otherwise the CWD. */
432
+ dir?: string;
433
+ /** Stale-by-mtime threshold (seconds). 0 disables TTL eviction. */
434
+ ttl_seconds?: number;
435
+ /** Hard cap on the number of JSON entries on disk. 0 disables. */
436
+ max_entries?: number;
437
+ /** Hard cap on cumulative file size (bytes). 0 disables. */
438
+ max_cache_bytes?: number;
439
+ }
440
+
441
+ interface WorkerToolsCfg {
442
+ cost_cap_usd?: unknown;
443
+ cost_cap_mode?: unknown;
444
+ }
445
+
446
+ interface BreakerCfg {
447
+ max_session_cost_usd?: number;
448
+ max_session_tokens?: number;
449
+ max_session_wall_seconds?: number;
450
+ max_dag_nodes?: number;
451
+ max_dag_depth?: number;
452
+ }
453
+
454
454
  interface ConfigPinningConfig {
455
455
  /** Pin file location. Repo-relative or absolute. */
456
456
  pin_file?: string;
@@ -1,50 +1,9 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
2
  import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
3
3
 
4
- /** A live bridge to a Python crosscheck-agent child. */
5
- interface BridgeHandle {
6
- /** Names of the tools exposed by the Python child (from its
7
- * `tools/list` response). */
8
- readonly toolNames: ReadonlySet<string>;
9
- /** PID of the spawned Python child process, or `null` if the
10
- * transport hasn't started yet / has already closed. Useful for
11
- * shutdown verification and host-side diagnostics. */
12
- readonly pid: number | null;
13
- /** Forward a `tools/call` to the Python child and return the result
14
- * envelope verbatim. */
15
- callTool(name: string, args: Record<string, unknown>): Promise<{
16
- content: {
17
- type: string;
18
- text: string;
19
- }[];
20
- isError?: boolean;
21
- }>;
22
- /** Re-fetch the tool list (in case the child surfaces new tools mid-
23
- * session). Returns the new tool names. */
24
- refreshTools(): Promise<ReadonlySet<string>>;
25
- /** Tear down the bridge cleanly. Idempotent — repeat calls are no-ops. */
26
- close(): Promise<void>;
27
- }
28
-
29
- /** Per-call node-cache config block. Mirrors Python's CFG.node_cache. */
30
- interface NodeCacheCfg {
31
- /** Master enable flag. Defaults to true when the cfg block exists
32
- * in opts; explicitly set false to short-circuit reads + writes. */
33
- enabled?: boolean;
34
- /** Directory to store cache entries. Relative paths are resolved
35
- * against `repoRoot` when supplied; otherwise the CWD. */
36
- dir?: string;
37
- /** Stale-by-mtime threshold (seconds). 0 disables TTL eviction. */
38
- ttl_seconds?: number;
39
- /** Hard cap on the number of JSON entries on disk. 0 disables. */
40
- max_entries?: number;
41
- /** Hard cap on cumulative file size (bytes). 0 disables. */
42
- max_cache_bytes?: number;
43
- }
44
-
45
- interface WorkerToolsCfg {
46
- cost_cap_usd?: unknown;
47
- cost_cap_mode?: unknown;
4
+ interface PricingDoc {
5
+ /** `<provider>` -> `<model>` -> ModelRates. */
6
+ [provider: string]: unknown;
48
7
  }
49
8
 
50
9
  /** sessions row. */
@@ -363,19 +322,6 @@ interface Storage extends StorageRead, StorageWrite {
363
322
  close(): Promise<void>;
364
323
  }
365
324
 
366
- interface BreakerCfg {
367
- max_session_cost_usd?: number;
368
- max_session_tokens?: number;
369
- max_session_wall_seconds?: number;
370
- max_dag_nodes?: number;
371
- max_dag_depth?: number;
372
- }
373
-
374
- interface PricingDoc {
375
- /** `<provider>` -> `<model>` -> ModelRates. */
376
- [provider: string]: unknown;
377
- }
378
-
379
325
  /** One provider call's token + cost record. Mirrors `Usage.to_dict()`
380
326
  * output exactly. */
381
327
  interface Usage {
@@ -451,6 +397,60 @@ interface Provider {
451
397
  send(args: SendArgs): Promise<SendResult>;
452
398
  }
453
399
 
400
+ /** A live bridge to a Python crosscheck-agent child. */
401
+ interface BridgeHandle {
402
+ /** Names of the tools exposed by the Python child (from its
403
+ * `tools/list` response). */
404
+ readonly toolNames: ReadonlySet<string>;
405
+ /** PID of the spawned Python child process, or `null` if the
406
+ * transport hasn't started yet / has already closed. Useful for
407
+ * shutdown verification and host-side diagnostics. */
408
+ readonly pid: number | null;
409
+ /** Forward a `tools/call` to the Python child and return the result
410
+ * envelope verbatim. */
411
+ callTool(name: string, args: Record<string, unknown>): Promise<{
412
+ content: {
413
+ type: string;
414
+ text: string;
415
+ }[];
416
+ isError?: boolean;
417
+ }>;
418
+ /** Re-fetch the tool list (in case the child surfaces new tools mid-
419
+ * session). Returns the new tool names. */
420
+ refreshTools(): Promise<ReadonlySet<string>>;
421
+ /** Tear down the bridge cleanly. Idempotent — repeat calls are no-ops. */
422
+ close(): Promise<void>;
423
+ }
424
+
425
+ /** Per-call node-cache config block. Mirrors Python's CFG.node_cache. */
426
+ interface NodeCacheCfg {
427
+ /** Master enable flag. Defaults to true when the cfg block exists
428
+ * in opts; explicitly set false to short-circuit reads + writes. */
429
+ enabled?: boolean;
430
+ /** Directory to store cache entries. Relative paths are resolved
431
+ * against `repoRoot` when supplied; otherwise the CWD. */
432
+ dir?: string;
433
+ /** Stale-by-mtime threshold (seconds). 0 disables TTL eviction. */
434
+ ttl_seconds?: number;
435
+ /** Hard cap on the number of JSON entries on disk. 0 disables. */
436
+ max_entries?: number;
437
+ /** Hard cap on cumulative file size (bytes). 0 disables. */
438
+ max_cache_bytes?: number;
439
+ }
440
+
441
+ interface WorkerToolsCfg {
442
+ cost_cap_usd?: unknown;
443
+ cost_cap_mode?: unknown;
444
+ }
445
+
446
+ interface BreakerCfg {
447
+ max_session_cost_usd?: number;
448
+ max_session_tokens?: number;
449
+ max_session_wall_seconds?: number;
450
+ max_dag_nodes?: number;
451
+ max_dag_depth?: number;
452
+ }
453
+
454
454
  interface ConfigPinningConfig {
455
455
  /** Pin file location. Repo-relative or absolute. */
456
456
  pin_file?: string;
@@ -1282,7 +1282,7 @@ init_esm_shims();
1282
1282
  var CREDIT_RE = /insufficient|no\s+credit|credits?|billing|quota|payment\s*required|exceeded your current quota|not\s+enough|fund|no active subscription|spend limit/i;
1283
1283
  function isCreditsFailure(status, bodyText) {
1284
1284
  if (status === 402) return true;
1285
- if (status === 401 || status === 403 || status === 429 || status === 400) {
1285
+ if (status === 401 || status === 403 || status === 400) {
1286
1286
  return CREDIT_RE.test(bodyText);
1287
1287
  }
1288
1288
  return false;
@@ -1319,7 +1319,7 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
1319
1319
  if (status === 429) {
1320
1320
  return new ProviderError(
1321
1321
  "rate_limit",
1322
- `${provider}: rate limited (HTTP 429). Detail: ${detail}`,
1322
+ `${provider}: rate limited (HTTP 429) \u2014 retrying with backoff. Note that some providers word a rate limit as a balance problem; check your balance before topping up. Detail: ${detail}`,
1323
1323
  { status, ...retryAfterS !== void 0 ? { retryAfterS } : {} }
1324
1324
  );
1325
1325
  }
@@ -2109,10 +2109,20 @@ function fallbackModelsFor(env, provider) {
2109
2109
  if (pinned && pinned !== DEFAULT_MODELS[provider]) return [];
2110
2110
  return DEFAULT_FALLBACKS[provider] ?? [];
2111
2111
  }
2112
+ var DEFAULT_EXCLUDED_PROVIDERS = ["mistral", "groq"];
2113
+ function excludedProviders(env) {
2114
+ const parse = (v) => (v ?? "").split(",").map((x) => x.trim().toLowerCase()).filter((x) => x !== "");
2115
+ const excluded = new Set(
2116
+ parse(env["CROSSCHECK_EXCLUDE_PROVIDERS"]).length > 0 ? parse(env["CROSSCHECK_EXCLUDE_PROVIDERS"]) : DEFAULT_EXCLUDED_PROVIDERS
2117
+ );
2118
+ for (const name of parse(env["CROSSCHECK_ENABLE_PROVIDERS"])) excluded.delete(name);
2119
+ return excluded;
2120
+ }
2112
2121
  function buildProviders(opts) {
2113
2122
  const out = {};
2123
+ const excluded = excludedProviders(opts.env);
2114
2124
  const anthropicKey = opts.env["ANTHROPIC_API_KEY"];
2115
- if (anthropicKey) {
2125
+ if (anthropicKey && !excluded.has("anthropic")) {
2116
2126
  const model = opts.env["ANTHROPIC_MODEL"] ?? DEFAULT_MODELS.anthropic;
2117
2127
  out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts, fallbackModelsFor(opts.env, "anthropic"));
2118
2128
  }
@@ -2128,11 +2138,12 @@ function buildProviders(opts) {
2128
2138
  for (const s of openAiCompatSpec) {
2129
2139
  const apiKey = opts.env[s.keyEnv];
2130
2140
  if (!apiKey) continue;
2141
+ if (excluded.has(s.name)) continue;
2131
2142
  const model = opts.env[s.modelEnv] ?? s.defaultModel;
2132
2143
  out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts, fallbackModelsFor(opts.env, s.name));
2133
2144
  }
2134
2145
  const geminiKey = opts.env["GEMINI_API_KEY"];
2135
- if (geminiKey) {
2146
+ if (geminiKey && !excluded.has("gemini")) {
2136
2147
  const model = opts.env["GEMINI_MODEL"] ?? DEFAULT_MODELS.gemini;
2137
2148
  out["gemini"] = makeGeminiProvider(model, geminiKey, opts, fallbackModelsFor(opts.env, "gemini"));
2138
2149
  }
@@ -2331,7 +2342,7 @@ import { z } from "zod";
2331
2342
  // src/server-meta.ts
2332
2343
  init_esm_shims();
2333
2344
  var SERVER_NAME = "crosscheck-agent";
2334
- var SERVER_VERSION = true ? "0.2.23" : "0.0.0-dev";
2345
+ var SERVER_VERSION = true ? "0.2.25" : "0.0.0-dev";
2335
2346
 
2336
2347
  // src/tools/audit.ts
2337
2348
  init_esm_shims();
@@ -6114,11 +6125,20 @@ ${ctxBody}` });
6114
6125
  });
6115
6126
  };
6116
6127
  const onAnswer = opts.onAnswer;
6117
- const workerCall = onAnswer ? async (p) => {
6128
+ const onPanelistStart = opts.onPanelistStart;
6129
+ const workerCall = onAnswer || onPanelistStart ? async (p) => {
6130
+ if (onPanelistStart) {
6131
+ try {
6132
+ onPanelistStart(p.name, p.model);
6133
+ } catch {
6134
+ }
6135
+ }
6118
6136
  const answer = await dispatchOne(p);
6119
- try {
6120
- onAnswer(answer, p.name);
6121
- } catch {
6137
+ if (onAnswer) {
6138
+ try {
6139
+ onAnswer(answer, p.name);
6140
+ } catch {
6141
+ }
6122
6142
  }
6123
6143
  return answer;
6124
6144
  } : dispatchOne;
@@ -10447,6 +10467,12 @@ ${prior.body}` });
10447
10467
  content: `This is round ${rnd} of ${maxRounds}. Reply for this round.`
10448
10468
  });
10449
10469
  for (const p of selected) {
10470
+ if (opts.onPanelistStart) {
10471
+ try {
10472
+ opts.onPanelistStart(p.name, p.model, rnd);
10473
+ } catch {
10474
+ }
10475
+ }
10450
10476
  const entry = await callPanelist(p, roundMessages);
10451
10477
  const withRound = { ...entry, round: rnd };
10452
10478
  transcript.push(withRound);
@@ -12910,7 +12936,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
12910
12936
  var DEFAULT_PACKAGE = "crosscheck-cli";
12911
12937
  var FETCH_TIMEOUT_MS = 3e3;
12912
12938
  function engineVersion() {
12913
- return true ? "0.2.23" : "0.0.0-dev";
12939
+ return true ? "0.2.25" : "0.0.0-dev";
12914
12940
  }
12915
12941
  function defaultUpdateCachePath() {
12916
12942
  const base = process.env["CROSSCHECK_DATA_DIR"] || path10.join(os.homedir() || os.tmpdir(), ".crosscheck");