mock-mcp 0.3.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +212 -124
  2. package/dist/adapter/index.cjs +875 -0
  3. package/dist/adapter/index.d.cts +142 -0
  4. package/dist/adapter/index.d.ts +142 -0
  5. package/dist/adapter/index.js +835 -0
  6. package/dist/client/connect.cjs +991 -0
  7. package/dist/client/connect.d.cts +218 -0
  8. package/dist/client/connect.d.ts +211 -7
  9. package/dist/client/connect.js +941 -20
  10. package/dist/client/index.cjs +992 -0
  11. package/dist/client/index.d.cts +3 -0
  12. package/dist/client/index.d.ts +3 -2
  13. package/dist/client/index.js +951 -2
  14. package/dist/daemon/index.cjs +717 -0
  15. package/dist/daemon/index.d.cts +62 -0
  16. package/dist/daemon/index.d.ts +62 -0
  17. package/dist/daemon/index.js +678 -0
  18. package/dist/index.cjs +2708 -0
  19. package/dist/index.d.cts +602 -0
  20. package/dist/index.d.ts +602 -11
  21. package/dist/index.js +2651 -53
  22. package/dist/shared/index.cjs +506 -0
  23. package/dist/shared/index.d.cts +241 -0
  24. package/dist/shared/index.d.ts +241 -0
  25. package/dist/shared/index.js +423 -0
  26. package/dist/types-bEGXLBF0.d.cts +190 -0
  27. package/dist/types-bEGXLBF0.d.ts +190 -0
  28. package/package.json +45 -4
  29. package/dist/client/batch-mock-collector.d.ts +0 -111
  30. package/dist/client/batch-mock-collector.js +0 -308
  31. package/dist/client/util.d.ts +0 -1
  32. package/dist/client/util.js +0 -3
  33. package/dist/connect.cjs +0 -400
  34. package/dist/connect.d.cts +0 -82
  35. package/dist/server/index.d.ts +0 -1
  36. package/dist/server/index.js +0 -1
  37. package/dist/server/test-mock-mcp-server.d.ts +0 -73
  38. package/dist/server/test-mock-mcp-server.js +0 -419
  39. package/dist/types.d.ts +0 -45
  40. package/dist/types.js +0 -2
@@ -0,0 +1,602 @@
1
+ /**
2
+ * Mock Bridge Daemon - The core singleton process for mock-mcp.
3
+ *
4
+ * Responsibilities:
5
+ * - Listen on IPC (Unix Domain Socket / Windows Named Pipe)
6
+ * - Accept test process connections via WebSocket /test
7
+ * - Accept adapter connections via HTTP /control (JSON-RPC)
8
+ * - Manage runs, batches, claims with lease-based concurrency control
9
+ * - Clean up expired claims and disconnected runs
10
+ */
11
+ type Logger$3 = Pick<Console, "log" | "warn" | "error"> & {
12
+ debug?: (...args: unknown[]) => void;
13
+ };
14
+ interface MockMcpDaemonOptions {
15
+ projectRoot: string;
16
+ token: string;
17
+ version: string;
18
+ cacheDir?: string;
19
+ logger?: Logger$3;
20
+ /** Default lease time for batch claims (ms). Default: 30000 */
21
+ defaultLeaseMs?: number;
22
+ /** Interval for sweeping expired claims (ms). Default: 5000 */
23
+ sweepIntervalMs?: number;
24
+ /** Auto-shutdown after this many ms of inactivity (no runs/adapters). Default: 600000 (10min) */
25
+ idleShutdownMs?: number;
26
+ }
27
+ declare class MockMcpDaemon {
28
+ private readonly logger;
29
+ private readonly opts;
30
+ private server?;
31
+ private wss?;
32
+ private sweepTimer?;
33
+ private idleTimer?;
34
+ private startedAt?;
35
+ private readonly runs;
36
+ private readonly batches;
37
+ private readonly pendingQueue;
38
+ private batchSeq;
39
+ constructor(options: MockMcpDaemonOptions);
40
+ start(): Promise<void>;
41
+ stop(): Promise<void>;
42
+ private handleHttp;
43
+ private readBody;
44
+ private handleWsConnection;
45
+ private handleBatchRequest;
46
+ private cleanupRun;
47
+ private handleRpc;
48
+ private getErrorCode;
49
+ private rpcSuccess;
50
+ private rpcError;
51
+ private getStatus;
52
+ private listRuns;
53
+ private claimNextBatch;
54
+ private provideBatch;
55
+ private validateMocks;
56
+ private releaseBatch;
57
+ private getBatch;
58
+ private sweepExpiredClaims;
59
+ private resetIdleTimer;
60
+ }
61
+
62
+ /**
63
+ * MCP Adapter - The MCP stdio server that bridges AI clients to the Daemon.
64
+ *
65
+ * Each MCP client (Cursor, Claude Desktop, etc.) spawns one Adapter process.
66
+ * The Adapter:
67
+ * - Exposes MCP tools (claim_next_batch, provide_batch_mock_data, get_status)
68
+ * - Discovers and connects to ALL active Daemons via IPC
69
+ * - Does NOT bind any ports (avoids conflicts)
70
+ * - Works across projects without requiring --project-root
71
+ */
72
+ type Logger$2 = Pick<Console, "log" | "warn" | "error"> & {
73
+ debug?: (...args: unknown[]) => void;
74
+ };
75
+ interface AdapterOptions {
76
+ logger?: Logger$2;
77
+ version?: string;
78
+ }
79
+ declare function runAdapter(opts?: AdapterOptions): Promise<void>;
80
+
81
+ /**
82
+ * Core types for mock-mcp.
83
+ */
84
+ /**
85
+ * Shape of a mock request emitted by the test process.
86
+ */
87
+ interface MockRequestDescriptor {
88
+ requestId: string;
89
+ endpoint: string;
90
+ method: string;
91
+ body?: unknown;
92
+ headers?: Record<string, string>;
93
+ metadata?: Record<string, unknown>;
94
+ }
95
+ /**
96
+ * Shape of the mock data that needs to be returned for a request.
97
+ */
98
+ interface MockResponseDescriptor {
99
+ requestId: string;
100
+ data: unknown;
101
+ status?: number;
102
+ headers?: Record<string, string>;
103
+ delayMs?: number;
104
+ }
105
+ /**
106
+ * Resolved mock with typed data.
107
+ */
108
+ interface ResolvedMock<T = unknown> extends Omit<MockResponseDescriptor, "data"> {
109
+ data: T;
110
+ }
111
+
112
+ /**
113
+ * Protocol definitions for mock-mcp Daemon communication.
114
+ *
115
+ * Defines message types for:
116
+ * - Test channel (WebSocket /test): Test process ↔ Daemon
117
+ * - Control channel (HTTP /control): Adapter ↔ Daemon (JSON-RPC)
118
+ */
119
+
120
+ /**
121
+ * Result of claimNextBatch RPC.
122
+ */
123
+ interface ClaimNextBatchResult {
124
+ batchId: string;
125
+ runId: string;
126
+ requests: MockRequestDescriptor[];
127
+ claimToken: string;
128
+ leaseUntil: number;
129
+ }
130
+ /**
131
+ * Result of provideBatch RPC.
132
+ */
133
+ interface ProvideBatchResult {
134
+ ok: boolean;
135
+ message?: string;
136
+ }
137
+ /**
138
+ * Result of getStatus RPC.
139
+ */
140
+ interface GetStatusResult {
141
+ version: string;
142
+ projectId: string;
143
+ projectRoot: string;
144
+ pid: number;
145
+ uptime: number;
146
+ runs: number;
147
+ pending: number;
148
+ claimed: number;
149
+ totalBatches: number;
150
+ }
151
+ /**
152
+ * Result of listRuns RPC.
153
+ */
154
+ interface ListRunsResult {
155
+ runs: RunInfo[];
156
+ }
157
+ interface RunInfo {
158
+ runId: string;
159
+ pid: number;
160
+ cwd: string;
161
+ startedAt: string;
162
+ lastSeen: number;
163
+ pendingBatches: number;
164
+ testMeta?: {
165
+ testFile?: string;
166
+ testName?: string;
167
+ };
168
+ }
169
+ /**
170
+ * Result of getBatch RPC.
171
+ */
172
+ interface GetBatchResult {
173
+ batchId: string;
174
+ runId: string;
175
+ requests: MockRequestDescriptor[];
176
+ status: BatchStatus;
177
+ createdAt: number;
178
+ claim?: {
179
+ adapterId: string;
180
+ leaseUntil: number;
181
+ };
182
+ }
183
+ type BatchStatus = "pending" | "claimed" | "fulfilled" | "expired";
184
+
185
+ /**
186
+ * Daemon Client - HTTP/JSON-RPC client for communicating with the Daemon.
187
+ *
188
+ * Used by the MCP Adapter to call daemon RPC methods.
189
+ */
190
+
191
+ declare class DaemonClient {
192
+ private readonly ipcPath;
193
+ private readonly token;
194
+ private readonly adapterId;
195
+ constructor(ipcPath: string, token: string, adapterId: string);
196
+ getStatus(): Promise<GetStatusResult>;
197
+ listRuns(): Promise<ListRunsResult>;
198
+ claimNextBatch(args: {
199
+ runId?: string;
200
+ leaseMs?: number;
201
+ }): Promise<ClaimNextBatchResult | null>;
202
+ provideBatch(args: {
203
+ batchId: string;
204
+ claimToken: string;
205
+ mocks: MockResponseDescriptor[];
206
+ }): Promise<ProvideBatchResult>;
207
+ releaseBatch(args: {
208
+ batchId: string;
209
+ claimToken: string;
210
+ reason?: string;
211
+ }): Promise<{
212
+ ok: boolean;
213
+ }>;
214
+ getBatch(batchId: string): Promise<GetBatchResult>;
215
+ private rpc;
216
+ }
217
+
218
+ /**
219
+ * Discovery module for mock-mcp Daemon + Adapter architecture.
220
+ *
221
+ * Provides:
222
+ * - Project root resolution (up-search for .git / package.json)
223
+ * - Project ID computation (sha256 of realpath)
224
+ * - Registry/lock file path management
225
+ * - IPC path (Unix Domain Socket / Windows Named Pipe)
226
+ * - ensureDaemonRunning() - atomic daemon startup with lock
227
+ */
228
+
229
+ interface DaemonRegistry {
230
+ projectId: string;
231
+ projectRoot: string;
232
+ ipcPath: string;
233
+ token: string;
234
+ pid: number;
235
+ startedAt: string;
236
+ version: string;
237
+ }
238
+ interface EnsureDaemonOptions {
239
+ projectRoot?: string;
240
+ timeoutMs?: number;
241
+ /** For testing: override cache directory */
242
+ cacheDir?: string;
243
+ }
244
+ /**
245
+ * Resolve the project root by searching upward for .git or package.json.
246
+ * Falls back to the starting directory if nothing is found.
247
+ */
248
+ declare function resolveProjectRoot(startDir?: string): string;
249
+ /**
250
+ * Compute a stable project ID from the project root path.
251
+ * Uses sha256 of the realpath, truncated to 16 characters.
252
+ */
253
+ declare function computeProjectId(projectRoot: string): string;
254
+ /**
255
+ * Ensure the daemon is running for the given project.
256
+ *
257
+ * This function:
258
+ * 1. Checks if a daemon is already running (via registry + health check)
259
+ * 2. If not, acquires a lock and spawns a new daemon
260
+ * 3. Waits for the daemon to become healthy
261
+ * 4. Returns the registry information
262
+ *
263
+ * Thread-safe: multiple processes calling this concurrently will only start one daemon.
264
+ */
265
+ declare function ensureDaemonRunning(opts?: EnsureDaemonOptions): Promise<DaemonRegistry>;
266
+ /**
267
+ * Entry in the global active daemons index.
268
+ */
269
+ interface ActiveDaemonEntry {
270
+ projectId: string;
271
+ projectRoot: string;
272
+ ipcPath: string;
273
+ registryPath: string;
274
+ pid: number;
275
+ startedAt: string;
276
+ version: string;
277
+ }
278
+ /**
279
+ * Global index of all active daemons.
280
+ */
281
+ interface ActiveDaemonsIndex {
282
+ daemons: ActiveDaemonEntry[];
283
+ updatedAt: string;
284
+ }
285
+ /**
286
+ * Read the global active daemons index.
287
+ */
288
+ declare function readGlobalIndex(cacheDir?: string): Promise<ActiveDaemonsIndex>;
289
+ /**
290
+ * Clean up stale entries from the global index.
291
+ * Removes entries where the daemon is no longer running.
292
+ */
293
+ declare function cleanupGlobalIndex(cacheDir?: string): Promise<void>;
294
+ /**
295
+ * Discover all active daemons.
296
+ * Returns list of daemons with their connection info.
297
+ */
298
+ declare function discoverAllDaemons(cacheDir?: string): Promise<{
299
+ registry: DaemonRegistry;
300
+ healthy: boolean;
301
+ }[]>;
302
+
303
+ /**
304
+ * MultiDaemonClient - Aggregates multiple daemon connections for cross-project operation.
305
+ *
306
+ * This client discovers all active daemons and can perform operations across them.
307
+ * It's designed for MCP adapters that need to work without knowing the specific project root.
308
+ */
309
+
310
+ type Logger$1 = Pick<Console, "log" | "warn" | "error"> & {
311
+ debug?: (...args: unknown[]) => void;
312
+ };
313
+ interface MultiDaemonClientOptions {
314
+ logger?: Logger$1;
315
+ cacheDir?: string;
316
+ }
317
+ interface DaemonConnection {
318
+ registry: DaemonRegistry;
319
+ healthy: boolean;
320
+ }
321
+ interface ExtendedRunInfo extends RunInfo {
322
+ projectId: string;
323
+ projectRoot: string;
324
+ }
325
+ interface AggregatedStatusResult {
326
+ daemons: (GetStatusResult & {
327
+ healthy: boolean;
328
+ })[];
329
+ totalRuns: number;
330
+ totalPending: number;
331
+ totalClaimed: number;
332
+ }
333
+ declare class MultiDaemonClient {
334
+ private readonly logger;
335
+ private readonly cacheDir?;
336
+ private readonly adapterId;
337
+ constructor(opts?: MultiDaemonClientOptions);
338
+ /**
339
+ * Discover all active and healthy daemons.
340
+ */
341
+ discoverDaemons(): Promise<DaemonConnection[]>;
342
+ /**
343
+ * Get aggregated status from all daemons.
344
+ */
345
+ getAggregatedStatus(): Promise<AggregatedStatusResult>;
346
+ /**
347
+ * List all runs across all daemons.
348
+ */
349
+ listAllRuns(): Promise<ExtendedRunInfo[]>;
350
+ /**
351
+ * Claim the next available batch from any daemon.
352
+ * Searches through all daemons in order until finding one with a pending batch.
353
+ */
354
+ claimNextBatch(args: {
355
+ runId?: string;
356
+ leaseMs?: number;
357
+ }): Promise<(ClaimNextBatchResult & {
358
+ projectId: string;
359
+ projectRoot: string;
360
+ }) | null>;
361
+ /**
362
+ * Provide mock data for a batch.
363
+ * Automatically routes to the correct daemon based on batchId.
364
+ */
365
+ provideBatch(args: {
366
+ batchId: string;
367
+ claimToken: string;
368
+ mocks: MockResponseDescriptor[];
369
+ }): Promise<ProvideBatchResult>;
370
+ /**
371
+ * Release a batch.
372
+ */
373
+ releaseBatch(args: {
374
+ batchId: string;
375
+ claimToken: string;
376
+ reason?: string;
377
+ }): Promise<{
378
+ ok: boolean;
379
+ message?: string;
380
+ }>;
381
+ /**
382
+ * Get a specific batch by ID.
383
+ */
384
+ getBatch(batchId: string): Promise<GetBatchResult | null>;
385
+ private rpc;
386
+ }
387
+
388
+ /**
389
+ * BatchMockCollector - Collects HTTP requests and sends them to the daemon for mock generation.
390
+ *
391
+ * Features:
392
+ * - Connects to daemon via IPC (Unix Domain Socket / Named Pipe)
393
+ * - Automatically discovers and starts daemon if needed
394
+ * - Uses runId for multi-run isolation
395
+ * - Sends HELLO handshake on connection
396
+ * - Batches concurrent requests for efficient processing
397
+ */
398
+
399
+ type Logger = Pick<Console, "log" | "warn" | "error"> & {
400
+ debug?: (...args: unknown[]) => void;
401
+ };
402
+ interface BatchMockCollectorOptions {
403
+ /**
404
+ * Timeout for individual mock requests in milliseconds.
405
+ * @default 60000
406
+ */
407
+ timeout?: number;
408
+ /**
409
+ * Delay (in milliseconds) before flushing the current batch.
410
+ * Setting to 0 flushes on the next macrotask.
411
+ * @default 0
412
+ */
413
+ batchDebounceMs?: number;
414
+ /**
415
+ * Maximum number of requests that may be included in a single batch.
416
+ * @default 50
417
+ */
418
+ maxBatchSize?: number;
419
+ /**
420
+ * Optional custom logger. Defaults to console.
421
+ */
422
+ logger?: Logger;
423
+ /**
424
+ * Interval for WebSocket heartbeats in milliseconds. Set to 0 to disable.
425
+ * @default 15000
426
+ */
427
+ heartbeatIntervalMs?: number;
428
+ /**
429
+ * Automatically attempt to reconnect when the WebSocket closes unexpectedly.
430
+ * @default true
431
+ */
432
+ enableReconnect?: boolean;
433
+ /**
434
+ * Optional project root override. By default, auto-detected from process.cwd().
435
+ * Use this when you want to explicitly specify the project root directory.
436
+ */
437
+ projectRoot?: string;
438
+ /**
439
+ * Path to the current test file. The project root will be auto-detected
440
+ * by searching upward for .git or package.json from this file's directory.
441
+ *
442
+ * This is useful in test frameworks like Jest/Vitest:
443
+ * @example
444
+ * ```typescript
445
+ * // Jest
446
+ * const mockMcpClient = await connect({
447
+ * filePath: expect.getState().testPath,
448
+ * });
449
+ *
450
+ * // Vitest
451
+ * const mockMcpClient = await connect({
452
+ * filePath: import.meta.url,
453
+ * });
454
+ * ```
455
+ *
456
+ * Note: If both `filePath` and `projectRoot` are provided, `projectRoot` takes precedence.
457
+ */
458
+ filePath?: string;
459
+ /**
460
+ * Optional test metadata to include in the HELLO message.
461
+ */
462
+ testMeta?: {
463
+ testFile?: string;
464
+ testName?: string;
465
+ };
466
+ }
467
+ interface RequestMockOptions {
468
+ body?: unknown;
469
+ headers?: Record<string, string>;
470
+ metadata?: Record<string, unknown>;
471
+ }
472
+ /**
473
+ * Collects HTTP requests and forwards them to the daemon for AI-assisted mock generation.
474
+ *
475
+ * New architecture:
476
+ * - Connects to daemon via IPC (not TCP port)
477
+ * - Sends HELLO handshake with runId
478
+ * - Uses the new protocol (BATCH_MOCK_REQUEST → BATCH_MOCK_RESULT)
479
+ */
480
+ declare class BatchMockCollector {
481
+ private ws?;
482
+ private registry?;
483
+ private readonly runId;
484
+ private readonly pendingRequests;
485
+ private readonly queuedRequestIds;
486
+ private readonly timeout;
487
+ private readonly batchDebounceMs;
488
+ private readonly maxBatchSize;
489
+ private readonly logger;
490
+ private readonly heartbeatIntervalMs;
491
+ private readonly enableReconnect;
492
+ private readonly projectRoot?;
493
+ private readonly testMeta?;
494
+ private batchTimer;
495
+ private heartbeatTimer;
496
+ private reconnectTimer;
497
+ private requestIdCounter;
498
+ private closed;
499
+ private authed;
500
+ private readyResolve?;
501
+ private readyReject?;
502
+ private readyPromise;
503
+ constructor(options?: BatchMockCollectorOptions);
504
+ /**
505
+ * Resolve projectRoot from options.
506
+ * Priority: projectRoot (if valid) > filePath > projectRoot (fallback) > undefined (auto-detect)
507
+ *
508
+ * A projectRoot is "valid" if it contains .git or package.json. This prevents
509
+ * accidentally using a wrong directory (e.g., user's home directory) when the
510
+ * caller mistakenly passes process.cwd() as projectRoot.
511
+ */
512
+ private resolveProjectRootFromOptions;
513
+ /**
514
+ * Check if a directory contains .git or package.json
515
+ */
516
+ private hasGitOrPackageJson;
517
+ /**
518
+ * Ensures the underlying connection is ready for use.
519
+ */
520
+ waitUntilReady(): Promise<void>;
521
+ /**
522
+ * Request mock data for a specific endpoint/method pair.
523
+ */
524
+ requestMock<T = unknown>(endpoint: string, method: string, options?: RequestMockOptions): Promise<ResolvedMock<T>>;
525
+ /**
526
+ * Wait for all currently pending requests to settle.
527
+ */
528
+ waitForPendingRequests(): Promise<void>;
529
+ /**
530
+ * Close the connection and fail all pending requests.
531
+ */
532
+ close(code?: number): Promise<void>;
533
+ private initConnection;
534
+ private createWebSocket;
535
+ private setupWebSocket;
536
+ private sendHello;
537
+ private handleMessage;
538
+ private isHelloAck;
539
+ private isBatchMockResult;
540
+ private resolveRequest;
541
+ private rejectRequest;
542
+ private failAllPending;
543
+ private enqueueRequest;
544
+ private flushQueue;
545
+ private sendBatch;
546
+ private startHeartbeat;
547
+ private stopHeartbeat;
548
+ private scheduleReconnect;
549
+ private resetReadyPromise;
550
+ private buildResolvedMock;
551
+ /**
552
+ * Get the run ID for this collector instance.
553
+ */
554
+ getRunId(): string;
555
+ /**
556
+ * Get the daemon registry information (after connection).
557
+ */
558
+ getRegistry(): DaemonRegistry | undefined;
559
+ }
560
+
561
+ /**
562
+ * Options for connecting to the mock-mcp daemon.
563
+ */
564
+ type ConnectOptions = BatchMockCollectorOptions | undefined;
565
+ /**
566
+ * Interface for the mock client returned by connect().
567
+ */
568
+ interface MockClient {
569
+ waitUntilReady(): Promise<void>;
570
+ requestMock<T = unknown>(endpoint: string, method: string, options?: RequestMockOptions): Promise<ResolvedMock<T>>;
571
+ waitForPendingRequests(): Promise<void>;
572
+ close(code?: number): Promise<void>;
573
+ /** Get the unique run ID for this connection */
574
+ getRunId(): string;
575
+ }
576
+ /**
577
+ * Connect to the mock-mcp daemon for AI-assisted mock generation.
578
+ *
579
+ * This function:
580
+ * 1. Automatically discovers or starts the daemon for your project
581
+ * 2. Establishes a WebSocket connection via IPC
582
+ * 3. Returns a MockClient for requesting mocks
583
+ *
584
+ * The daemon uses Unix Domain Sockets (macOS/Linux) or Named Pipes (Windows)
585
+ * instead of TCP ports, eliminating port conflicts when multiple MCP clients
586
+ * are running simultaneously.
587
+ *
588
+ * @example
589
+ * ```typescript
590
+ * import { connect } from 'mock-mcp';
591
+ *
592
+ * const client = await connect();
593
+ *
594
+ * const mock = await client.requestMock<User>('/api/users/1', 'GET');
595
+ * console.log(mock.data); // AI-generated mock data
596
+ *
597
+ * await client.close();
598
+ * ```
599
+ */
600
+ declare const connect: (options?: ConnectOptions) => Promise<MockClient>;
601
+
602
+ export { type ActiveDaemonEntry, type ActiveDaemonsIndex, type AdapterOptions, type AggregatedStatusResult, BatchMockCollector, type BatchMockCollectorOptions, type ConnectOptions, DaemonClient, type DaemonRegistry, type ExtendedRunInfo, type MockClient, MockMcpDaemon, type MockMcpDaemonOptions, type MockRequestDescriptor, type MockResponseDescriptor, MultiDaemonClient, type MultiDaemonClientOptions, type RequestMockOptions, type ResolvedMock, cleanupGlobalIndex, computeProjectId, connect, discoverAllDaemons, ensureDaemonRunning, readGlobalIndex, resolveProjectRoot, runAdapter };