@harness-fe/mcp-server 4.0.0-next.2 → 4.0.0-next.4

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 (100) hide show
  1. package/dist/bin.d.ts +2 -0
  2. package/dist/bin.js +15 -0
  3. package/dist/daemon.d.ts +3 -3
  4. package/dist/daemon.js +1 -1
  5. package/dist/index.d.ts +4 -4
  6. package/dist/index.js +3 -3
  7. package/dist/mcp.d.ts +2 -2
  8. package/dist/mcp.js +65 -19
  9. package/dist/mcpHttp.d.ts +2 -2
  10. package/dist/mcpHttp.js +88 -18
  11. package/package.json +5 -7
  12. package/src/bin.ts +19 -0
  13. package/src/daemon.ts +3 -3
  14. package/src/experimental.test.ts +2 -2
  15. package/src/index.ts +4 -4
  16. package/src/mcp.ts +67 -23
  17. package/src/mcpHttp.test.ts +52 -3
  18. package/src/mcpHttp.ts +102 -23
  19. package/src/mcpLayer.e2e.test.ts +2 -2
  20. package/src/newCapabilities.e2e.test.ts +3 -3
  21. package/dist/auth.d.ts +0 -53
  22. package/dist/auth.js +0 -212
  23. package/dist/bridge.d.ts +0 -323
  24. package/dist/bridge.js +0 -1618
  25. package/dist/cli.d.ts +0 -18
  26. package/dist/cli.js +0 -293
  27. package/dist/dashboardApi.d.ts +0 -40
  28. package/dist/dashboardApi.js +0 -142
  29. package/dist/dashboardSpa.d.ts +0 -18
  30. package/dist/dashboardSpa.js +0 -180
  31. package/dist/dashboardUrl.d.ts +0 -13
  32. package/dist/dashboardUrl.js +0 -18
  33. package/dist/eventsHandler.d.ts +0 -24
  34. package/dist/eventsHandler.js +0 -114
  35. package/dist/identity.d.ts +0 -90
  36. package/dist/identity.js +0 -123
  37. package/dist/openBrowser.d.ts +0 -33
  38. package/dist/openBrowser.js +0 -63
  39. package/dist/remoteBridge.d.ts +0 -61
  40. package/dist/remoteBridge.js +0 -307
  41. package/dist/replayCreate.d.ts +0 -36
  42. package/dist/replayCreate.js +0 -156
  43. package/dist/replayViewer.d.ts +0 -20
  44. package/dist/replayViewer.js +0 -168
  45. package/dist/sessionRouter.d.ts +0 -45
  46. package/dist/sessionRouter.js +0 -88
  47. package/dist/store/JsonMemoryStore.d.ts +0 -52
  48. package/dist/store/JsonMemoryStore.js +0 -119
  49. package/dist/store/JsonTaskStore.d.ts +0 -21
  50. package/dist/store/JsonTaskStore.js +0 -53
  51. package/dist/store/JsonlStore.d.ts +0 -128
  52. package/dist/store/JsonlStore.js +0 -1172
  53. package/dist/store/MemoryEventStore.d.ts +0 -47
  54. package/dist/store/MemoryEventStore.js +0 -111
  55. package/dist/store/WriteQueue.d.ts +0 -51
  56. package/dist/store/WriteQueue.js +0 -142
  57. package/dist/store/index.d.ts +0 -6
  58. package/dist/store/index.js +0 -5
  59. package/dist/store/types.d.ts +0 -427
  60. package/dist/store/types.js +0 -19
  61. package/dist/visitorTimeline.d.ts +0 -24
  62. package/dist/visitorTimeline.js +0 -68
  63. package/src/auth.test.ts +0 -90
  64. package/src/auth.ts +0 -248
  65. package/src/bridge-auth.test.ts +0 -196
  66. package/src/bridge.test.ts +0 -1708
  67. package/src/bridge.ts +0 -1854
  68. package/src/cli.ts +0 -338
  69. package/src/dashboardApi.test.ts +0 -235
  70. package/src/dashboardApi.ts +0 -184
  71. package/src/dashboardSpa.test.ts +0 -239
  72. package/src/dashboardSpa.ts +0 -195
  73. package/src/dashboardUrl.test.ts +0 -46
  74. package/src/dashboardUrl.ts +0 -28
  75. package/src/eventsHandler.test.ts +0 -247
  76. package/src/eventsHandler.ts +0 -136
  77. package/src/identity.test.ts +0 -109
  78. package/src/identity.ts +0 -137
  79. package/src/openBrowser.test.ts +0 -103
  80. package/src/openBrowser.ts +0 -81
  81. package/src/remoteBridge.test.ts +0 -119
  82. package/src/remoteBridge.ts +0 -404
  83. package/src/replay.test.ts +0 -271
  84. package/src/replayCreate.ts +0 -194
  85. package/src/replayViewer.ts +0 -173
  86. package/src/sessionRouter.ts +0 -119
  87. package/src/store/JsonMemoryStore.test.ts +0 -175
  88. package/src/store/JsonMemoryStore.ts +0 -128
  89. package/src/store/JsonTaskStore.test.ts +0 -212
  90. package/src/store/JsonTaskStore.ts +0 -59
  91. package/src/store/JsonlStore.test.ts +0 -1538
  92. package/src/store/JsonlStore.ts +0 -1325
  93. package/src/store/MemoryEventStore.test.ts +0 -119
  94. package/src/store/MemoryEventStore.ts +0 -151
  95. package/src/store/WriteQueue.ts +0 -165
  96. package/src/store/identityTagging.test.ts +0 -67
  97. package/src/store/index.ts +0 -29
  98. package/src/store/types.ts +0 -532
  99. package/src/visitorTimeline.test.ts +0 -197
  100. package/src/visitorTimeline.ts +0 -89
package/dist/bridge.d.ts DELETED
@@ -1,323 +0,0 @@
1
- /**
2
- * WS bridge — accepts connections from vite-plugin and runtime-client.
3
- *
4
- * Protocol: see @harness-fe/protocol.
5
- *
6
- * Responsibilities:
7
- * - Handshake: `hello` frame → register peer in SessionRouter, reply `hello.ack`
8
- * - sendCommand(): forward a CommandFrame to the target tab, return a
9
- * Promise that resolves when the matching ResponseFrame arrives
10
- * - onEvent(): broadcast event frames to subscribers (mcp tools / future
11
- * recorder)
12
- */
13
- import { type IncomingMessage, type ServerResponse } from 'node:http';
14
- import { type AuthOptions } from './auth.js';
15
- import { type Principal } from './identity.js';
16
- import { type ConsentPolicy, type EventFrame, type HttpBatch, type TabInfo, type Task, type TaskStatus } from '@harness-fe/protocol';
17
- import { SessionRouter, type PeerSession } from './sessionRouter.js';
18
- import { type IStore, type ITaskStore, type IMemoryStore, type RetentionPolicy } from './store/index.js';
19
- /**
20
- * Surface used by the stdio MCP layer. Same shape whether the underlying
21
- * implementation is an in-process `Bridge` (leader) or a `RemoteBridge`
22
- * proxying over WS to another daemon (follower).
23
- *
24
- * All methods are async so the same call site works in both modes.
25
- */
26
- export interface IBridge {
27
- sendCommand(command: string, args: unknown, opts?: SendCommandOptions): Promise<unknown>;
28
- listTabs(): Promise<TabInfo[]>;
29
- listTasks(filter?: {
30
- status?: TaskStatus | 'all';
31
- limit?: number;
32
- }): Promise<Task[]>;
33
- claimTask(id: string, principal?: Principal): Promise<Task | undefined>;
34
- resolveTask(id: string, note?: string, principal?: Principal): Promise<Task | undefined>;
35
- getMemoryStore(): IMemoryStore;
36
- /**
37
- * Base URL (e.g. http://127.0.0.1:47729) where the replay viewer is reachable.
38
- * Returns undefined when the bridge does not serve HTTP (e.g. follower mode).
39
- */
40
- getViewerBaseUrl(): string | undefined;
41
- /**
42
- * The configured auth token, or undefined if auth is disabled. Used to
43
- * compose URLs that the user (or agent) can hit without an extra
44
- * authentication step.
45
- */
46
- getAuthToken(): string | undefined;
47
- /**
48
- * Read an attachment PNG for a task. Returns base64-encoded PNG or null.
49
- * The task must exist in the in-memory map so we can look up its projectId.
50
- */
51
- getTaskAttachmentData(taskId: string, attachmentId: string): Promise<string | null>;
52
- }
53
- export interface SendCommandOptions {
54
- tabId?: string;
55
- timeoutMs?: number;
56
- target?: 'runtime-client' | 'vite-plugin';
57
- projectId?: string;
58
- }
59
- /**
60
- * Default data directory for all persistence stores, keyed by the port the
61
- * daemon listens on. Identity of a daemon = its listening address; same
62
- * port → same on-disk store; different ports → independent stores.
63
- *
64
- * This lets users opt into isolation simply by configuring a different
65
- * `--port` in their `mcp.json`, and lets multiple IDEs targeting the same
66
- * port automatically share state through the existing leader/follower
67
- * mechanism in `cli.ts`. No cwd / project-root detection involved.
68
- */
69
- export declare function defaultDataDir(port: number): string;
70
- export interface BridgeOptions {
71
- port?: number;
72
- /** Bind address. Default 127.0.0.1 (no remote exposure). */
73
- host?: string;
74
- /**
75
- * Token-based auth applied to every HTTP route and WS upgrade. Empty
76
- * token disables auth (only valid when bound to a loopback host).
77
- */
78
- auth?: AuthOptions;
79
- /**
80
- * Browser-consent policy for control commands (4.0 · P2). When omitted it
81
- * defaults to `session` while auth is enabled (non-loopback / exposed) and
82
- * `off` on loopback — so solo dev keeps its zero-friction flow and any
83
- * exposed daemon prompts the user before an agent drives the page.
84
- */
85
- consent?: ConsentPolicy;
86
- /**
87
- * Override the host used when building outbound URLs (dashboard links,
88
- * replay viewer URLs). When omitted and `host` is `0.0.0.0` / `::`, the
89
- * first non-internal IPv4 address from the OS network interfaces is
90
- * used so other LAN devices can follow the links. For loopback binds the
91
- * literal `host` is reused.
92
- */
93
- publicHost?: string;
94
- /**
95
- * Store instance for JSONL persistence. If omitted, a default JsonlStore
96
- * is created at `dataDir` (or `defaultDataDir(port)` when `dataDir` is
97
- * also omitted). Pass null to disable persistence.
98
- */
99
- store?: IStore | null;
100
- /**
101
- * Task store instance for JSON task persistence. If omitted, a default
102
- * JsonTaskStore is created at `dataDir`. Pass null to disable task
103
- * persistence (useful in tests).
104
- */
105
- taskStore?: ITaskStore | null;
106
- /**
107
- * Memory store instance for agent memory persistence. If omitted, a default
108
- * JsonMemoryStore is created at `dataDir`. Pass null to disable memory
109
- * persistence (useful in tests).
110
- */
111
- memoryStore?: IMemoryStore | null;
112
- /**
113
- * Root data directory for task attachment binaries. Defaults to the same
114
- * `~/.harness/data` directory used by the stores. Override in tests.
115
- */
116
- attachmentsDataDir?: string;
117
- /**
118
- * Root data directory for the default stores (when `store` / `taskStore`
119
- * / `memoryStore` are not supplied). When omitted, computed from `port`
120
- * via `defaultDataDir(port)`. Set explicitly to point all stores at a
121
- * non-default location (useful for tests or migration).
122
- */
123
- dataDir?: string;
124
- /**
125
- * Optional friendly label surfaced in the startup banner and (later) the
126
- * dashboard title. Purely cosmetic — has no effect on data isolation,
127
- * routing, or auth. Identity is always the listening port.
128
- */
129
- label?: string;
130
- /**
131
- * Automatic retention policy enforcement.
132
- *
133
- * Without this, manual `session.purge` MCP calls are the only thing that
134
- * trims the on-disk store — so a long-running daemon will eventually fill
135
- * the user's disk. Default: run `store.purge()` once shortly after start
136
- * and every hour thereafter. Set `enabled: false` for tests / one-shot runs.
137
- */
138
- autoPurge?: {
139
- enabled?: boolean;
140
- /** Period between purges in ms. Default 1 hour. */
141
- intervalMs?: number;
142
- /** Override the retention policy. Default uses store's built-in defaults. */
143
- policy?: RetentionPolicy;
144
- /** Skip the startup purge (still runs the periodic timer). Default false. */
145
- skipInitial?: boolean;
146
- };
147
- }
148
- export type EventListener = (event: EventFrame, session: PeerSession) => void;
149
- export declare class Bridge implements IBridge {
150
- readonly router: SessionRouter;
151
- readonly store: IStore | null;
152
- readonly taskStore: ITaskStore | null;
153
- readonly memoryStore: IMemoryStore;
154
- private wss?;
155
- private httpServer?;
156
- /**
157
- * Optional HTTP handler invoked for non-WebSocket requests. Set via
158
- * `setHttpHandler()`. Allows higher layers (e.g. replay viewer) to serve
159
- * routes on the same port as the WS bridge without coupling Bridge to them.
160
- */
161
- private httpHandler?;
162
- private sockets;
163
- private pending;
164
- private eventListeners;
165
- private tasks;
166
- private opts;
167
- private auth;
168
- /** Browser-consent policy pushed to runtime clients in hello.ack (4.0 · P2). */
169
- private readonly consentPolicy;
170
- private publicHostOverride;
171
- private readonly attachDataDir;
172
- private autoPurgeOpts;
173
- /** Set by start() when auto-purge is enabled; cleared by stop(). */
174
- private autoPurgeTimer?;
175
- /**
176
- * Map from connectionId → buildId (for build-plugin connections)
177
- * or sessionId (for runtime-client connections).
178
- */
179
- private connToStoreId;
180
- /** Caller identity per connection (4.0 · P1). Resolved at WS upgrade. */
181
- private connToPrincipal;
182
- /**
183
- * Identity attributed to MCP-driven writes (task claim/resolve). The MCP
184
- * tool layer is a daemon-wide singleton today with no per-call caller
185
- * context, so stdio/HTTP agents collapse to one principal. P4 (per-session
186
- * MCP transport) replaces this with the real per-call principal.
187
- */
188
- private readonly defaultPrincipal;
189
- /** Connections that already logged a "no store session" warning. */
190
- private warnedNoSession;
191
- /**
192
- * Grace period timers: projectId → timer handle.
193
- * When a build plugin disconnects, a 30-second timer is started.
194
- * If the same project reconnects within that window, the timer is cancelled.
195
- */
196
- private graceTimers;
197
- /**
198
- * Pending build end info: projectId → { buildId, closedAt }.
199
- * Tracks builds waiting for the grace period to expire.
200
- */
201
- private pendingEndBuild;
202
- /**
203
- * Dashboard SPA subscribers — connections that sent `hello` with
204
- * role: 'dashboard-client'. Receive `dashboard.update` frames whenever
205
- * session state changes; never receive commands and never send events.
206
- */
207
- private dashboardSubscribers;
208
- /** Debounce per-session 'session.update' broadcasts so chatty rrweb chunks don't spam subscribers. */
209
- private dashboardDebounceTimers;
210
- /** Optional friendly label (HARNESS_FE_LABEL). Cosmetic only. */
211
- readonly label: string | undefined;
212
- constructor(opts?: BridgeOptions);
213
- /**
214
- * Returns the memory store instance for use by mcp.ts and other callers.
215
- */
216
- getMemoryStore(): IMemoryStore;
217
- private loadTasks;
218
- private persistTasks;
219
- /**
220
- * Load tasks for a specific project from the task store into the in-memory map.
221
- * Called when a project connects so its tasks are available immediately.
222
- */
223
- private loadTasksForProject;
224
- private taskDedupKey;
225
- /**
226
- * Register an HTTP request handler that runs for non-WebSocket requests on
227
- * the same port. Only one handler is supported; later calls replace prior
228
- * ones. WS upgrades bypass this handler.
229
- */
230
- setHttpHandler(handler: (req: IncomingMessage, res: ServerResponse) => void | Promise<void>): void;
231
- /**
232
- * Insert a handler that runs *before* the main HTTP handler. Return `true`
233
- * if the request was consumed (no further processing); return `false` to
234
- * fall through to the existing handler. Allows mcpHttp.ts to mount on
235
- * `/mcp` without owning the whole HTTP surface.
236
- */
237
- prependHttpHandler(handler: (req: IncomingMessage, res: ServerResponse) => Promise<boolean> | boolean): void;
238
- start(): Promise<void>;
239
- stop(): Promise<void>;
240
- /**
241
- * Run `store.purge()` defensively. Errors are logged but never bubble out
242
- * — the daemon must continue serving even if disk is full or files are
243
- * locked.
244
- */
245
- private runAutoPurge;
246
- /** Expose the bound port (useful when port:0 was passed for tests). */
247
- getBoundPort(): number | undefined;
248
- getViewerBaseUrl(): string | undefined;
249
- getAuthToken(): string | undefined;
250
- /** Daemon auth options — used by the MCP layer to identify the per-call principal (4.0 · P4). */
251
- getAuthOptions(): AuthOptions;
252
- /**
253
- * Broadcast a `dashboard.update` frame to every subscribed dashboard SPA.
254
- *
255
- * `kind: 'session.update'` is debounced per-sessionId (200ms) so chatty
256
- * rrweb chunk appends don't spam every subscriber. Other kinds fire
257
- * immediately because they represent rare state transitions (new
258
- * session, session closed, export created).
259
- */
260
- notifyDashboard(payload: {
261
- kind: 'session.new' | 'session.update' | 'session.closed' | 'project.update' | 'export.new';
262
- sessionId?: string;
263
- projectId?: string;
264
- }): void;
265
- private flushDashboardUpdate;
266
- /**
267
- * Host string used when handing out URLs that other machines need to
268
- * reach. Loopback binds keep the literal address; wildcard binds
269
- * (0.0.0.0 / ::) prefer the first non-internal IPv4. Explicit
270
- * `publicHost` always wins.
271
- */
272
- private getPublicHost;
273
- onEvent(listener: EventListener): () => void;
274
- /**
275
- * Handle an HTTP-batch POST /events request (Edge Runtime path).
276
- *
277
- * Stateless: each call is a self-contained hello+events sequence.
278
- * The hello is used to register the peer (or look up the existing session)
279
- * and the events are persisted to the session timeline — same paths as the
280
- * WS handler.
281
- */
282
- handleHttpBatch(hello: HttpBatch['hello'], events: HttpBatch['events']): void;
283
- listTabs(): Promise<TabInfo[]>;
284
- listTasks(filter?: {
285
- status?: TaskStatus | 'all';
286
- limit?: number;
287
- }): Promise<Task[]>;
288
- claimTask(id: string, principal?: Principal): Promise<Task | undefined>;
289
- getTaskAttachmentData(taskId: string, attachmentId: string): Promise<string | null>;
290
- resolveTask(id: string, note?: string, principal?: Principal): Promise<Task | undefined>;
291
- private persistTaskEvent;
292
- private recordTask;
293
- /**
294
- * Write attachment data to disk and return persisted pointer objects.
295
- * Drops attachments if the total decoded size exceeds 4 MB.
296
- */
297
- private writeTaskAttachments;
298
- /**
299
- * Read an attachment from disk for a given task.
300
- * Returns the base64 data if found, null otherwise.
301
- */
302
- readTaskAttachment(projectId: string, taskId: string, attachmentId: string): string | null;
303
- /**
304
- * Send a command to a specific tab and await its response.
305
- * `tabId` falls back to the most-recent active tab if omitted.
306
- */
307
- sendCommand(command: string, args: unknown, opts?: SendCommandOptions): Promise<unknown>;
308
- /**
309
- * Returns true if there is an active build for the given projectId.
310
- * Checks both in-memory grace period builds and the store.
311
- */
312
- private hasActiveBuild;
313
- private onConnection;
314
- private handleFrame;
315
- /**
316
- * Runtime → daemon query dispatcher (0.5+). Whitelisted methods only.
317
- * Owner check: tasks.update / tasks.get / tasks.delete refuse to touch
318
- * tasks whose `visitorId` doesn't match the caller's `peer.visitorId`.
319
- */
320
- private handleQuery;
321
- private handleMcpCall;
322
- private invokeMcpMethod;
323
- }