@juspay/neurolink 12.49.15 → 12.49.17

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.
@@ -82,6 +82,10 @@ export class MCPClientFactory {
82
82
  * Enhanced with retry logic, rate limiting, and circuit breaker protection
83
83
  */
84
84
  static async createClient(config, timeout = DEFAULT_CLIENT_TIMEOUT) {
85
+ const connectionTimeout = config.httpOptions?.connectionTimeout;
86
+ if (connectionTimeout !== undefined) {
87
+ timeout = Math.min(timeout, connectionTimeout);
88
+ }
85
89
  const startTime = Date.now();
86
90
  const { traceId, parentSpanId } = getActiveTraceContext();
87
91
  const obsSpan = SpanSerializer.createSpan(SpanType.MCP_TRANSPORT, "mcp.connect", {
@@ -208,8 +212,17 @@ export class MCPClientFactory {
208
212
  const client = new Client(this.NEUROLINK_IMPLEMENTATION, {
209
213
  capabilities: this.DEFAULT_CAPABILITIES,
210
214
  });
215
+ const requestTimeout = config.httpOptions?.requestTimeout;
216
+ if (requestTimeout !== undefined) {
217
+ const request = client.request.bind(client);
218
+ client.request = (payload, resultSchema, options) => request(payload, resultSchema, {
219
+ ...options,
220
+ timeout: Math.min(requestTimeout, options?.timeout ?? requestTimeout),
221
+ maxTotalTimeout: Math.min(requestTimeout, options?.maxTotalTimeout ?? requestTimeout),
222
+ });
223
+ }
211
224
  // Connect with timeout
212
- await this.raceWithTimeout(client.connect(transport), timeout, `Client connection timeout for ${config.id}`);
225
+ await this.raceWithTimeout(client.connect(transport, { timeout }), timeout, `Client connection timeout for ${config.id}`);
213
226
  // Perform handshake to get server capabilities
214
227
  const serverCapabilities = await this.performHandshake(client, timeout);
215
228
  mcpLogger.debug(`[MCPClientFactory] Handshake completed for ${config.id}`, {
@@ -331,7 +344,13 @@ export class MCPClientFactory {
331
344
  try {
332
345
  const url = new URL(config.url);
333
346
  const { SSEClientTransport } = await import("@modelcontextprotocol/sdk/client/sse.js");
334
- const transport = new SSEClientTransport(url);
347
+ const transportFetch = this.createEnhancedFetch(config, config.httpOptions?.requestTimeout ?? 60000);
348
+ const transport = new SSEClientTransport(url, {
349
+ requestInit: { headers: config.headers },
350
+ fetch: transportFetch,
351
+ // EventSource prefers this fetch over the transport-level hook for GET.
352
+ eventSourceInit: { fetch: transportFetch },
353
+ });
335
354
  return { transport };
336
355
  }
337
356
  catch (error) {
@@ -415,12 +434,25 @@ export class MCPClientFactory {
415
434
  /**
416
435
  * Create a fetch wrapper with timeout support
417
436
  */
418
- static createFetchWithTimeout(timeoutMs) {
437
+ static createFetchWithTimeout(timeoutMs, transportFetch) {
419
438
  return async (input, init) => {
420
439
  const controller = new AbortController();
421
440
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
422
441
  try {
423
- return await fetch(input, { ...init, signal: controller.signal });
442
+ const response = await transportFetch(input, {
443
+ ...init,
444
+ // EventSource requests "follow"; enforcing this here covers its GET
445
+ // as well as MCP POSTs and prevents credentials crossing a redirect.
446
+ redirect: "manual",
447
+ signal: init?.signal
448
+ ? AbortSignal.any([controller.signal, init.signal])
449
+ : controller.signal,
450
+ });
451
+ if (response.status >= 300 && response.status < 400) {
452
+ await response.body?.cancel();
453
+ throw new Error("MCP transport redirects are not permitted");
454
+ }
455
+ return response;
424
456
  }
425
457
  finally {
426
458
  clearTimeout(timeoutId);
@@ -431,7 +463,7 @@ export class MCPClientFactory {
431
463
  * Create an enhanced fetch function with timeout and optional retry
432
464
  */
433
465
  static createEnhancedFetch(config, timeoutMs, oauthProvider) {
434
- const fetchWithTimeout = this.createFetchWithTimeout(timeoutMs);
466
+ const fetchWithTimeout = this.createFetchWithTimeout(timeoutMs, config.fetch ?? globalThis.fetch);
435
467
  return async (input, init) => {
436
468
  // If OAuth is configured, ensure we have valid tokens
437
469
  if (oauthProvider && config.auth?.type === "oauth2") {
@@ -28,8 +28,10 @@ export declare class ToolDiscoveryService extends EventEmitter {
28
28
  setOutputNormalizer(normalizer: McpOutputNormalizer): void;
29
29
  /**
30
30
  * Discover tools from an external MCP server
31
+ * Startup probes use the fresh connection directly under the same operation
32
+ * deadline, so an ordinary discovery cooldown cannot exhaust reconnects.
31
33
  */
32
- discoverTools(serverId: string, client: Client, timeout?: number): Promise<ToolDiscoveryResult>;
34
+ discoverTools(serverId: string, client: Client, timeout?: number, isCurrentDiscovery?: () => boolean, startupDiscovery?: boolean): Promise<ToolDiscoveryResult>;
33
35
  /**
34
36
  * Perform the actual tool discovery
35
37
  */
@@ -106,15 +106,21 @@ export class ToolDiscoveryService extends EventEmitter {
106
106
  }
107
107
  /**
108
108
  * Discover tools from an external MCP server
109
+ * Startup probes use the fresh connection directly under the same operation
110
+ * deadline, so an ordinary discovery cooldown cannot exhaust reconnects.
109
111
  */
110
- async discoverTools(serverId, client, timeout = DEFAULT_TOOL_TIMEOUT) {
112
+ async discoverTools(serverId, client, timeout = DEFAULT_TOOL_TIMEOUT, isCurrentDiscovery = () => true, startupDiscovery = false) {
111
113
  return withSpan({
112
114
  name: "neurolink.mcp.discoverTools",
113
115
  tracer: tracers.mcp,
114
116
  attributes: { "mcp.server_id": serverId },
115
117
  }, async (span) => {
116
118
  const startTime = Date.now();
119
+ let ownsDiscovery = false;
117
120
  try {
121
+ if (!isCurrentDiscovery()) {
122
+ throw new Error(`Discovery owner for '${serverId}' is no longer current`);
123
+ }
118
124
  // Prevent concurrent discovery for same server
119
125
  if (this.discoveryInProgress.has(serverId)) {
120
126
  return {
@@ -127,19 +133,23 @@ export class ToolDiscoveryService extends EventEmitter {
127
133
  };
128
134
  }
129
135
  this.discoveryInProgress.add(serverId);
136
+ ownsDiscovery = true;
130
137
  mcpLogger.info(`[ToolDiscoveryService] Starting tool discovery for server: ${serverId}`);
131
- // Create circuit breaker for tool discovery
132
- const circuitBreaker = globalCircuitBreakerManager.getBreaker(`tool-discovery-${serverId}`, {
133
- failureThreshold: 2,
134
- resetTimeout: 60000,
135
- operationTimeout: timeout,
136
- });
137
- // Discover tools with circuit breaker protection
138
- const tools = await circuitBreaker.execute(async () => {
139
- return await this.performToolDiscovery(serverId, client, timeout);
140
- });
138
+ const discover = () => this.performToolDiscovery(serverId, client, timeout);
139
+ // A new connection must get its bounded readiness probe even while
140
+ // the ordinary discovery breaker is cooling down. Its transport and
141
+ // operation deadlines still apply; real failures still reject startup.
142
+ const tools = startupDiscovery
143
+ ? await discover()
144
+ : await globalCircuitBreakerManager
145
+ .getBreaker(`tool-discovery-${serverId}`, {
146
+ failureThreshold: 2,
147
+ resetTimeout: 60000,
148
+ operationTimeout: timeout,
149
+ })
150
+ .execute(discover);
141
151
  // Register discovered tools
142
- const registeredTools = await this.registerDiscoveredTools(serverId, tools);
152
+ const registeredTools = await this.registerDiscoveredTools(serverId, tools, isCurrentDiscovery);
143
153
  span.setAttribute("mcp.tools_discovered", registeredTools.length);
144
154
  const result = {
145
155
  success: true,
@@ -182,7 +192,9 @@ export class ToolDiscoveryService extends EventEmitter {
182
192
  };
183
193
  }
184
194
  finally {
185
- this.discoveryInProgress.delete(serverId);
195
+ if (ownsDiscovery) {
196
+ this.discoveryInProgress.delete(serverId);
197
+ }
186
198
  }
187
199
  });
188
200
  }
@@ -201,13 +213,19 @@ export class ToolDiscoveryService extends EventEmitter {
201
213
  /**
202
214
  * Register discovered tools
203
215
  */
204
- async registerDiscoveredTools(serverId, tools) {
216
+ async registerDiscoveredTools(serverId, tools, isCurrentDiscovery) {
205
217
  const registeredTools = [];
218
+ if (!isCurrentDiscovery()) {
219
+ throw new Error(`Discovery owner for '${serverId}' is no longer current`);
220
+ }
206
221
  // Clear existing tools for this server
207
222
  this.clearServerTools(serverId);
208
223
  for (const tool of tools) {
209
224
  try {
210
225
  const toolInfo = await this.createToolInfo(serverId, tool);
226
+ if (!isCurrentDiscovery()) {
227
+ throw new Error(`Discovery owner for '${serverId}' is no longer current`);
228
+ }
211
229
  const validation = this.validateTool(toolInfo);
212
230
  if (!validation.isValid) {
213
231
  mcpLogger.warn(`[ToolDiscoveryService] Skipping invalid tool ${tool.name} from ${serverId}:`, validation.errors);
@@ -232,11 +250,7 @@ export class ToolDiscoveryService extends EventEmitter {
232
250
  }
233
251
  // Add tool if not already present
234
252
  if (!serverTools.find((t) => t.name === tool.name)) {
235
- serverTools.push({
236
- name: tool.name,
237
- description: tool.description || "",
238
- inputSchema: tool.inputSchema,
239
- });
253
+ serverTools.push(toolInfo);
240
254
  }
241
255
  // Track server tools (legacy)
242
256
  if (!this.serverTools.has(serverId)) {
@@ -257,9 +271,15 @@ export class ToolDiscoveryService extends EventEmitter {
257
271
  mcpLogger.debug(`[ToolDiscoveryService] Registered tool: ${tool.name} from ${serverId}`);
258
272
  }
259
273
  catch (error) {
274
+ if (!isCurrentDiscovery()) {
275
+ throw error;
276
+ }
260
277
  mcpLogger.error(`[ToolDiscoveryService] Failed to register tool ${tool.name} from ${serverId}:`, error);
261
278
  }
262
279
  }
280
+ if (!isCurrentDiscovery()) {
281
+ throw new Error(`Discovery owner for '${serverId}' is no longer current`);
282
+ }
263
283
  return registeredTools;
264
284
  }
265
285
  /**
@@ -271,6 +291,7 @@ export class ToolDiscoveryService extends EventEmitter {
271
291
  description: tool.description || "No description provided",
272
292
  serverId,
273
293
  inputSchema: tool.inputSchema,
294
+ ...(tool.annotations ? { annotations: { ...tool.annotations } } : {}),
274
295
  isAvailable: true,
275
296
  stats: {
276
297
  totalCalls: 0,
@@ -782,6 +803,7 @@ export class ToolDiscoveryService extends EventEmitter {
782
803
  description: tool.description,
783
804
  serverId,
784
805
  inputSchema: tool.inputSchema,
806
+ ...(tool.annotations ? { annotations: { ...tool.annotations } } : {}),
785
807
  isAvailable: true,
786
808
  stats: {
787
809
  totalCalls: 0,
@@ -819,6 +841,7 @@ export class ToolDiscoveryService extends EventEmitter {
819
841
  description: tool.description,
820
842
  serverId,
821
843
  inputSchema: tool.inputSchema,
844
+ ...(tool.annotations ? { annotations: { ...tool.annotations } } : {}),
822
845
  isAvailable: true,
823
846
  stats: {
824
847
  totalCalls: 0,
@@ -6,7 +6,7 @@
6
6
  * Uses real MCP infrastructure for tool discovery and execution.
7
7
  */
8
8
  import type { DecisionCallerOptions, DecisionLimitsQuery, DecisionLimitsReading, DecisionOptions, DecisionResult, AgentDefinition, AgentNetworkConfig, AgentRunOptions, AgentRunOutcome, AgentToolRegistrationOptions, IsolatedAgentDefinition, NetworkExecutionInput, NetworkExecutionOptions, NetworkExecutionResult, NetworkStreamChunk, WorkerInstanceOptions } from "./types/index.js";
9
- import type { CompactionConfig, CompactionResult, ArtifactStore, SpanData, ObservabilityConfig, MetricsSummary, MCPToolAnnotations, TraceView, AuthenticatedContext, AuthProvider, JsonObject, NeuroLinkEvents, TypedEventEmitter, MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig, ChatMessage, ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPToolInfo, GenerateOptions, GenerateResult, ProviderStatus, TextGenerationOptions, TextGenerationResult, MCPExecutableTool, MCPServerInfo, MCPStatus, StreamOptions, StreamResult, ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions, BatchOperationResult, StreamGenerationEndContext, ToolRoutingServerDescriptor, ToolDedupConfig, ToolConfig, KnowledgeEngineStatus, ChecklistState, ArtifactPageRequest, BankArtifactOptions, BankedArtifactRef, DelegateCollectRequest, DelegateCollectResult, DelegateHandle, DelegateRegistrationOptions, DelegateSpawnOptions, BackgroundCommandHandle, BackgroundCommandOptions, BackgroundCommandOutputPage, BackgroundCommandPageRequest, BackgroundCommandPolicy, BackgroundCommandStatus, GitToolResult, GitToolsetOptions } from "./types/index.js";
9
+ import type { CompactionConfig, CompactionResult, ArtifactStore, SpanData, ObservabilityConfig, MetricsSummary, MCPToolAnnotations, TraceView, AuthenticatedContext, AuthProvider, JsonObject, NeuroLinkEvents, TypedEventEmitter, MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig, ChatMessage, ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPServerHealth, ExternalMCPToolInfo, GenerateOptions, GenerateResult, ProviderStatus, TextGenerationOptions, TextGenerationResult, MCPExecutableTool, MCPServerInfo, MCPStatus, StreamOptions, StreamResult, ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions, BatchOperationResult, StreamGenerationEndContext, ToolRoutingServerDescriptor, ToolDedupConfig, ToolConfig, KnowledgeEngineStatus, ChecklistState, ArtifactPageRequest, BankArtifactOptions, BankedArtifactRef, DelegateCollectRequest, DelegateCollectResult, DelegateHandle, DelegateRegistrationOptions, DelegateSpawnOptions, BackgroundCommandHandle, BackgroundCommandOptions, BackgroundCommandOutputPage, BackgroundCommandPageRequest, BackgroundCommandPolicy, BackgroundCommandStatus, GitToolResult, GitToolsetOptions } from "./types/index.js";
10
10
  import { ConversationMemoryManager } from "./core/conversationMemoryManager.js";
11
11
  import type { RedisConversationMemoryManager } from "./core/redisConversationMemoryManager.js";
12
12
  import { ExternalServerManager } from "./mcp/externalServerManager.js";
@@ -2150,6 +2150,7 @@ export declare class NeuroLink {
2150
2150
  * List all external MCP servers
2151
2151
  * @returns Array of server health information
2152
2152
  */
2153
+ getExternalMCPServerHealth(): ExternalMCPServerHealth[];
2153
2154
  listExternalMCPServers(): Array<{
2154
2155
  serverId: string;
2155
2156
  status: string;
package/dist/neurolink.js CHANGED
@@ -2306,6 +2306,9 @@ Current user's request: ${currentInput}`;
2306
2306
  * Setup event handlers for external server manager
2307
2307
  */
2308
2308
  setupExternalServerEventHandlers(constructorId) {
2309
+ this.externalServerManager.on("healthCheck", (event) => {
2310
+ this.emitter.emit("externalMCP:serverHealth", event);
2311
+ });
2309
2312
  this.externalServerManager.on("connected", (event) => {
2310
2313
  logger.debug(`[NeuroLink] 🔗 EXTERNAL_SERVER_EVENT_CONNECTED`, {
2311
2314
  constructorId,
@@ -12812,6 +12815,9 @@ Current user's request: ${currentInput}`;
12812
12815
  * List all external MCP servers
12813
12816
  * @returns Array of server health information
12814
12817
  */
12818
+ getExternalMCPServerHealth() {
12819
+ return this.externalServerManager.getServerStatuses();
12820
+ }
12815
12821
  listExternalMCPServers() {
12816
12822
  const serverStatuses = this.externalServerManager.getServerStatuses();
12817
12823
  const allServers = this.externalServerManager.listServers();
@@ -10,7 +10,7 @@ import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
10
10
  /**
11
11
  * Supported MCP transport protocols - imported from mcpTypes.js (canonical definition)
12
12
  */
13
- import type { MCPTransportType, MCPServerInfo, MCPServerReadiness } from "./mcp.js";
13
+ import type { MCPTransportType, MCPServerInfo, MCPServerReadiness, MCPToolAnnotations } from "./mcp.js";
14
14
  export type { MCPTransportType } from "./mcp.js";
15
15
  /**
16
16
  * External MCP server configuration for process spawning
@@ -40,6 +40,8 @@ export type ExternalMCPServerConfig = {
40
40
  url?: string;
41
41
  /** HTTP headers for authentication and configuration (HTTP/SSE/WebSocket) */
42
42
  headers?: Record<string, string>;
43
+ /** Trusted HTTP/SSE egress hook, retained for every connection and request. */
44
+ fetch?: typeof globalThis.fetch;
43
45
  /** List of tool names to block/blacklist from this server */
44
46
  blockedTools?: string[];
45
47
  /** Additional metadata */
@@ -114,6 +116,8 @@ export type ExternalMCPToolInfo = {
114
116
  serverId: string;
115
117
  /** Input schema (JSON Schema) */
116
118
  inputSchema?: JsonObject;
119
+ /** Server-supplied behavior hints; absent annotations remain absent. */
120
+ annotations?: MCPToolAnnotations;
117
121
  /** Whether the tool is currently available */
118
122
  isAvailable: boolean;
119
123
  /** Tool metadata */
@@ -147,6 +151,12 @@ export type ExternalMCPServerHealth = {
147
151
  toolCount: number;
148
152
  /** Any health issues detected */
149
153
  issues: string[];
154
+ /** Consecutive failed connections or live health checks, reset on recovery. */
155
+ consecutiveFailures?: number;
156
+ /** Most recent connection/health error, absent after successful recovery. */
157
+ lastError?: string;
158
+ /** Next automatic retry deadline, absent when no retry is scheduled. */
159
+ nextRetryAt?: Date;
150
160
  /** Performance metrics */
151
161
  performance: {
152
162
  uptime: number;
@@ -22,6 +22,8 @@ export type HITLConfig = {
22
22
  allowArgumentModification?: boolean;
23
23
  /** Auto-approve requests when they timeout (default: false - rejects on timeout) */
24
24
  autoApproveOnTimeout?: boolean;
25
+ /** Exact tool names that require an explicit answer even when timeout auto-approval is enabled. */
26
+ autoApproveExclusions?: string[];
25
27
  /** Enable audit logging for compliance and debugging (default: false) */
26
28
  auditLogging?: boolean;
27
29
  /** Advanced custom rules for complex tool scenarios (default: []) */
@@ -54,6 +54,12 @@ export type MCPServerInfo = {
54
54
  env?: Record<string, string>;
55
55
  url?: string;
56
56
  headers?: Record<string, string>;
57
+ /**
58
+ * Trusted host-provided fetch for HTTP and SSE, including reconnects and tool
59
+ * calls. Use a guarded, DNS-pinned dispatcher for untrusted server URLs.
60
+ * Programmatic only: functions cannot be persisted in JSON configuration.
61
+ */
62
+ fetch?: typeof globalThis.fetch;
57
63
  /** HTTP transport-specific options */
58
64
  httpOptions?: MCPHTTPTransportOptions;
59
65
  timeout?: number;
@@ -132,12 +138,12 @@ export type MCPServerInfo = {
132
138
  };
133
139
  };
134
140
  /**
135
- * HTTP Transport Options for fine-grained control
141
+ * Transport deadlines (all transports), plus HTTP connection-pool options
136
142
  */
137
143
  export type MCPHTTPTransportOptions = {
138
- /** Connection timeout in milliseconds (default: 30000) */
144
+ /** Explicit connection cap in milliseconds for every transport; absent retains the server/client startup budget. */
139
145
  connectionTimeout?: number;
140
- /** Request timeout in milliseconds (default: 60000) */
146
+ /** Explicit whole-request cap in milliseconds for every transport; absent retains caller/server RPC limits (HTTP fetch defaults are separate). */
141
147
  requestTimeout?: number;
142
148
  /** Idle timeout for connection pool (default: 120000) */
143
149
  idleTimeout?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.49.15",
3
+ "version": "12.49.17",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
6
6
  "author": {
@@ -83,6 +83,9 @@
83
83
  "test:harness-offline-timeout": "pnpm exec tsx test/continuous-test-suite-harness-offline-timeout.ts",
84
84
  "test:resolve-request-kind": "pnpm exec tsx test/continuous-test-suite-resolve-request-kind.ts",
85
85
  "test:mcp": "pnpm exec tsx test/continuous-test-suite-mcp-infra.ts",
86
+ "test:mcp:egress": "pnpm exec tsx test/continuous-test-suite-mcp-egress.ts",
87
+ "test:mcp:deadline-health": "pnpm exec tsx test/continuous-test-suite-mcp-deadline-health.ts",
88
+ "test:mcp:timeout-compat": "pnpm exec tsx test/continuous-test-suite-mcp-timeout-compat.ts",
86
89
  "test:tool-resolution": "pnpm exec tsx test/continuous-test-suite-tool-resolution.ts",
87
90
  "test:mcp:http": "pnpm exec tsx test/continuous-test-suite-mcp-http.ts",
88
91
  "test:mcp:sdk": "pnpm exec tsx test/continuous-test-suite-mcp-sdk.ts",
@@ -188,6 +191,7 @@
188
191
  "test:json-e2e": "pnpm exec tsx test/continuous-test-suite-json-e2e.ts",
189
192
  "test:workflow": "pnpm exec tsx test/continuous-test-suite-workflow.ts",
190
193
  "test:hitl": "pnpm exec tsx test/continuous-test-suite-hitl.ts",
194
+ "test:hitl:timeout-exclusions": "pnpm exec tsx test/continuous-test-suite-hitl-timeout-exclusions.ts",
191
195
  "test:tasks": "pnpm exec tsx test/continuous-test-suite-tasks.ts",
192
196
  "test:auth": "pnpm exec tsx test/continuous-test-suite-auth.ts",
193
197
  "test:autoresearch": "pnpm exec tsx test/continuous-test-suite-autoresearch.ts",
@@ -301,7 +305,8 @@
301
305
  "test:native-audio-model-aware": "pnpm exec tsx test/continuous-test-suite-native-audio-model-aware.ts",
302
306
  "test:cli-json-output": "pnpm exec tsx test/continuous-test-suite-cli-json-output.ts",
303
307
  "test:logging-guidelines": "pnpm exec tsx test/continuous-test-suite-logging-guidelines.ts",
304
- "test:proxy-polyfills": "pnpm exec tsx test/continuous-test-suite-proxy-polyfills.ts"
308
+ "test:proxy-polyfills": "pnpm exec tsx test/continuous-test-suite-proxy-polyfills.ts",
309
+ "test:mcp:startup-readiness": "pnpm exec tsx test/continuous-test-suite-mcp-startup-readiness.ts"
305
310
  },
306
311
  "files": [
307
312
  "dist",