@fractary/faber-mcp 1.1.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/README.md ADDED
@@ -0,0 +1,523 @@
1
+ # FABER MCP Server
2
+
3
+ A unified MCP (Model Context Protocol) server that provides complete FABER workflow orchestration and event logging capabilities. This server exposes 10 MCP tools for workflow control, state management, and event tracking, enabling AI agents to interact with the FABER workflow framework.
4
+
5
+ ## Features
6
+
7
+ ### Workflow Orchestration (6 Tools)
8
+ - **workflow_run**: Execute complete FABER workflows (Frame → Architect → Build → Evaluate → Release)
9
+ - **workflow_status**: Query workflow progress and current state
10
+ - **workflow_resume**: Resume paused workflows from where they left off
11
+ - **workflow_pause**: Pause running workflows for later resumption
12
+ - **workflow_recover**: Recover failed workflows from checkpoints or specific phases
13
+ - **workflow_cleanup**: Clean up old completed/failed workflow state files
14
+
15
+ ### Event Logging (4 Tools)
16
+ - **event_emit**: Log workflow events with rich metadata, artifacts, and error tracking
17
+ - **run_get**: Retrieve run details and current state
18
+ - **run_list**: Query and list workflow runs with filtering
19
+ - **events_consolidate**: Export events to JSONL format for archival or analysis
20
+
21
+ ### MCP Resources
22
+ - `faber://runs` - List all workflow runs
23
+ - `faber://runs/{run_id}` - Get run details
24
+ - `faber://runs/{run_id}/events` - Get run event log
25
+
26
+ ## Prerequisites
27
+
28
+ - Node.js >= 18.0.0
29
+ - npm or yarn
30
+
31
+ ## Installation
32
+
33
+ ### From Workspace (Development)
34
+
35
+ ```bash
36
+ # From repository root
37
+ npm install
38
+ npm run build -w mcp/server
39
+ ```
40
+
41
+ ### From npm (Production)
42
+
43
+ ```bash
44
+ npm install @fractary/faber-mcp
45
+ ```
46
+
47
+ ## Configuration
48
+
49
+ The server uses environment variables for configuration:
50
+
51
+ | Variable | Default | Description |
52
+ |----------|---------|-------------|
53
+ | `FABER_RUNS_PATH` | `.fractary/plugins/faber/runs` | Base path for run storage |
54
+
55
+ ## Usage
56
+
57
+ ### Starting the Server
58
+
59
+ ```bash
60
+ # Production (requires build)
61
+ npm start
62
+
63
+ # Development (with tsx)
64
+ npm run dev
65
+
66
+ # From workspace
67
+ npm run start -w mcp/server
68
+ ```
69
+
70
+ The server communicates via stdio, making it suitable for integration with MCP clients.
71
+
72
+ ### MCP Client Configuration
73
+
74
+ Add to your MCP client configuration (e.g., `~/.config/claude/mcp.json`):
75
+
76
+ ```json
77
+ {
78
+ "mcpServers": {
79
+ "fractary-faber": {
80
+ "command": "node",
81
+ "args": ["/path/to/faber/mcp/server/dist/server.js"],
82
+ "env": {
83
+ "FABER_RUNS_PATH": ".fractary/plugins/faber/runs"
84
+ }
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ ### Using the Binary
91
+
92
+ ```json
93
+ {
94
+ "mcpServers": {
95
+ "fractary-faber": {
96
+ "command": "fractary-faber-mcp",
97
+ "env": {
98
+ "FABER_RUNS_PATH": ".fractary/plugins/faber/runs"
99
+ }
100
+ }
101
+ }
102
+ }
103
+ ```
104
+
105
+ ## Tools Reference
106
+
107
+ ### Workflow Tools
108
+
109
+ #### fractary_faber_workflow_run
110
+
111
+ Run a complete FABER workflow for a work item.
112
+
113
+ **Parameters:**
114
+ | Name | Type | Required | Default | Description |
115
+ |------|------|----------|---------|-------------|
116
+ | `work_id` | string | Yes | - | Work item ID to process (e.g., "123" for issue #123) |
117
+ | `autonomy` | string | No | "assisted" | Autonomy level: dry-run, assisted, guarded, autonomous |
118
+ | `config` | object | No | - | Optional workflow configuration overrides |
119
+
120
+ **Example:**
121
+ ```json
122
+ {
123
+ "work_id": "123",
124
+ "autonomy": "assisted",
125
+ "config": {
126
+ "phases": {
127
+ "build": {
128
+ "skip_tests": false
129
+ }
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ #### fractary_faber_workflow_status
136
+
137
+ Get workflow status and progress.
138
+
139
+ **Parameters:**
140
+ | Name | Type | Required | Description |
141
+ |------|------|----------|-------------|
142
+ | `workflow_id` | string | No* | Workflow ID to check (e.g., "WF-abc123") |
143
+ | `work_id` | string | No* | Work item ID to find active workflow for |
144
+
145
+ *At least one of `workflow_id` or `work_id` must be provided.
146
+
147
+ **Example:**
148
+ ```json
149
+ {
150
+ "work_id": "123"
151
+ }
152
+ ```
153
+
154
+ #### fractary_faber_workflow_resume
155
+
156
+ Resume a paused workflow.
157
+
158
+ **Parameters:**
159
+ | Name | Type | Required | Description |
160
+ |------|------|----------|-------------|
161
+ | `workflow_id` | string | Yes | Workflow ID to resume |
162
+
163
+ **Example:**
164
+ ```json
165
+ {
166
+ "workflow_id": "WF-abc123"
167
+ }
168
+ ```
169
+
170
+ #### fractary_faber_workflow_pause
171
+
172
+ Pause a running workflow.
173
+
174
+ **Parameters:**
175
+ | Name | Type | Required | Description |
176
+ |------|------|----------|-------------|
177
+ | `workflow_id` | string | Yes | Workflow ID to pause |
178
+
179
+ **Example:**
180
+ ```json
181
+ {
182
+ "workflow_id": "WF-abc123"
183
+ }
184
+ ```
185
+
186
+ #### fractary_faber_workflow_recover
187
+
188
+ Recover a failed workflow from a checkpoint or specific phase.
189
+
190
+ **Parameters:**
191
+ | Name | Type | Required | Description |
192
+ |------|------|----------|-------------|
193
+ | `workflow_id` | string | Yes | Workflow ID to recover |
194
+ | `checkpoint_id` | string | No | Optional checkpoint ID to recover from |
195
+ | `from_phase` | string | No | Optional phase to restart from (frame, architect, build, evaluate, release) |
196
+ | `skip_phases` | array | No | Phases to skip during recovery |
197
+
198
+ **Example:**
199
+ ```json
200
+ {
201
+ "workflow_id": "WF-abc123",
202
+ "from_phase": "build"
203
+ }
204
+ ```
205
+
206
+ #### fractary_faber_workflow_cleanup
207
+
208
+ Clean up old completed/failed workflow state files.
209
+
210
+ **Parameters:**
211
+ | Name | Type | Required | Default | Description |
212
+ |------|------|----------|---------|-------------|
213
+ | `max_age_days` | number | No | 30 | Delete workflows older than this many days |
214
+
215
+ **Example:**
216
+ ```json
217
+ {
218
+ "max_age_days": 60
219
+ }
220
+ ```
221
+
222
+ ### Event Tools
223
+
224
+ #### fractary_faber_event_emit
225
+
226
+ Emit a workflow event to a FABER run.
227
+
228
+ **Parameters:**
229
+ | Name | Type | Required | Description |
230
+ |------|------|----------|-------------|
231
+ | `run_id` | string | Yes | Full run identifier (org/project/uuid) |
232
+ | `type` | string | Yes | Event type (see Event Types below) |
233
+ | `phase` | string | No | Current workflow phase |
234
+ | `step` | string | No | Current step within phase |
235
+ | `status` | string | No | Event status (started, completed, failed, skipped) |
236
+ | `message` | string | No | Human-readable description |
237
+ | `metadata` | object | No | Additional structured metadata |
238
+ | `artifacts` | array | No | Artifact references |
239
+ | `error` | object | No | Error details if status is "failed" |
240
+ | `agent_id` | string | No | Agent that emitted the event |
241
+
242
+ **Example:**
243
+ ```json
244
+ {
245
+ "run_id": "fractary/faber/550e8400-e29b-41d4-a716-446655440000",
246
+ "type": "phase_started",
247
+ "phase": "build",
248
+ "status": "started",
249
+ "message": "Starting build phase"
250
+ }
251
+ ```
252
+
253
+ #### fractary_faber_run_get
254
+
255
+ Retrieve run details and current state.
256
+
257
+ **Parameters:**
258
+ | Name | Type | Required | Default | Description |
259
+ |------|------|----------|---------|-------------|
260
+ | `run_id` | string | Yes | - | Full run identifier |
261
+ | `include_events` | boolean | No | false | Include event log in response |
262
+
263
+ **Example:**
264
+ ```json
265
+ {
266
+ "run_id": "fractary/faber/550e8400-e29b-41d4-a716-446655440000",
267
+ "include_events": true
268
+ }
269
+ ```
270
+
271
+ #### fractary_faber_run_list
272
+
273
+ Query and list workflow runs.
274
+
275
+ **Parameters:**
276
+ | Name | Type | Required | Default | Description |
277
+ |------|------|----------|---------|-------------|
278
+ | `limit` | number | No | 50 | Maximum number of runs to return |
279
+ | `status` | string | No | - | Filter by run status |
280
+ | `work_id` | string | No | - | Filter by work item ID |
281
+ | `since` | string | No | - | Filter runs created after this ISO timestamp |
282
+
283
+ **Example:**
284
+ ```json
285
+ {
286
+ "limit": 10,
287
+ "status": "running"
288
+ }
289
+ ```
290
+
291
+ #### fractary_faber_events_consolidate
292
+
293
+ Export events to JSONL format.
294
+
295
+ **Parameters:**
296
+ | Name | Type | Required | Description |
297
+ |------|------|----------|-------------|
298
+ | `run_id` | string | Yes | Full run identifier |
299
+ | `output_path` | string | No | Optional output file path (defaults to run directory) |
300
+
301
+ **Example:**
302
+ ```json
303
+ {
304
+ "run_id": "fractary/faber/550e8400-e29b-41d4-a716-446655440000"
305
+ }
306
+ ```
307
+
308
+ ## Event Types
309
+
310
+ The following event types are supported:
311
+
312
+ ### Workflow Control
313
+ - `workflow_started` - Workflow execution began
314
+ - `workflow_completed` - Workflow finished successfully
315
+ - `workflow_failed` - Workflow terminated with error
316
+ - `workflow_paused` - Workflow paused by user
317
+ - `workflow_resumed` - Workflow resumed from pause
318
+
319
+ ### Phase Events
320
+ - `phase_started` - FABER phase began (frame, architect, build, evaluate, release)
321
+ - `phase_completed` - Phase finished successfully
322
+ - `phase_failed` - Phase terminated with error
323
+ - `phase_skipped` - Phase skipped due to configuration
324
+
325
+ ### Step Events
326
+ - `step_started` - Individual step began
327
+ - `step_completed` - Step finished successfully
328
+ - `step_failed` - Step terminated with error
329
+
330
+ ### Integration Events
331
+ - `work_created` - Work item created in tracker
332
+ - `work_updated` - Work item updated
333
+ - `branch_created` - Git branch created
334
+ - `commit_created` - Git commit created
335
+ - `pr_created` - Pull request created
336
+ - `pr_updated` - Pull request updated
337
+ - `pr_merged` - Pull request merged
338
+
339
+ ### Agent Events
340
+ - `agent_started` - AI agent began execution
341
+ - `agent_completed` - Agent finished successfully
342
+ - `agent_failed` - Agent terminated with error
343
+ - `tool_called` - MCP tool invoked
344
+ - `prompt_sent` - Prompt sent to LLM
345
+ - `prompt_received` - Response received from LLM
346
+
347
+ ### Artifact Events
348
+ - `artifact_created` - File or output artifact created
349
+ - `artifact_updated` - Artifact modified
350
+ - `spec_created` - Specification document created
351
+ - `spec_updated` - Specification modified
352
+
353
+ ### Error Events
354
+ - `error` - General error occurred
355
+ - `validation_failed` - Input validation failed
356
+ - `timeout` - Operation timed out
357
+ - `retry` - Operation being retried
358
+
359
+ ## Data Structure
360
+
361
+ ### Run State
362
+
363
+ ```typescript
364
+ interface RunState {
365
+ run_id: string; // org/project/uuid
366
+ work_id: string; // Work item ID
367
+ status: "pending" | "running" | "completed" | "failed" | "paused";
368
+ start_time: string; // ISO timestamp
369
+ end_time?: string; // ISO timestamp
370
+ current_phase?: string; // frame, architect, build, evaluate, release
371
+ metadata: {
372
+ project: string;
373
+ organization: string;
374
+ workflow_type: string;
375
+ autonomy: string;
376
+ };
377
+ artifacts?: Array<{
378
+ type: string;
379
+ path: string;
380
+ created_at: string;
381
+ }>;
382
+ }
383
+ ```
384
+
385
+ ### Event Structure
386
+
387
+ ```typescript
388
+ interface FaberEvent {
389
+ event_id: string; // Monotonic integer
390
+ run_id: string; // org/project/uuid
391
+ timestamp: string; // ISO timestamp
392
+ type: string; // Event type (see Event Types)
393
+ phase?: string; // Workflow phase
394
+ step?: string; // Step within phase
395
+ status?: string; // started, completed, failed, skipped
396
+ message?: string; // Human-readable description
397
+ metadata?: Record<string, unknown>;
398
+ artifacts?: Array<{
399
+ type: string;
400
+ path: string;
401
+ }>;
402
+ error?: {
403
+ code: string;
404
+ message: string;
405
+ stack?: string;
406
+ };
407
+ agent_id?: string; // Agent identifier
408
+ }
409
+ ```
410
+
411
+ ## Migration from Event Gateway
412
+
413
+ If you're migrating from `@fractary/faber-event-gateway`, note the following changes:
414
+
415
+ ### Tool Name Changes
416
+
417
+ | Old Name | New Name |
418
+ |----------|----------|
419
+ | `emit_event` | `fractary_faber_event_emit` |
420
+ | `get_run` | `fractary_faber_run_get` |
421
+ | `list_runs` | `fractary_faber_run_list` |
422
+ | `consolidate_events` | `fractary_faber_events_consolidate` |
423
+
424
+ ### Package Name
425
+
426
+ - Old: `@fractary/faber-event-gateway`
427
+ - New: `@fractary/faber-mcp`
428
+
429
+ ### Server Name
430
+
431
+ - Old: `faber-event-gateway`
432
+ - New: `fractary-faber`
433
+
434
+ ### Behavior Changes
435
+
436
+ - All existing event tools retain the same functionality
437
+ - 6 new workflow orchestration tools added
438
+ - Resource URIs unchanged: `faber://runs/*`
439
+
440
+ ## Development
441
+
442
+ ### Build
443
+
444
+ ```bash
445
+ npm run build
446
+ ```
447
+
448
+ ### Watch Mode
449
+
450
+ ```bash
451
+ npm run watch
452
+ ```
453
+
454
+ ### Type Checking
455
+
456
+ ```bash
457
+ npm run typecheck
458
+ ```
459
+
460
+ ### Testing
461
+
462
+ ```bash
463
+ npm test
464
+ npm run test:watch
465
+ npm run test:coverage
466
+ ```
467
+
468
+ ### Linting
469
+
470
+ ```bash
471
+ npm run lint
472
+ ```
473
+
474
+ ## Architecture
475
+
476
+ ```
477
+ ┌─────────────────────────────────────┐
478
+ │ MCP Client (Claude Code, etc) │
479
+ └─────────────────────────────────────┘
480
+
481
+ │ MCP Protocol (stdio)
482
+
483
+ ┌─────────────────────────────────────┐
484
+ │ @fractary/faber-mcp │
485
+ │ ┌────────────┬─────────────────┐ │
486
+ │ │ workflow.ts│ events.ts │ │
487
+ │ │ (6 tools) │ (4 tools) │ │
488
+ │ └────────────┴─────────────────┘ │
489
+ │ ┌─────────────────────────────┐ │
490
+ │ │ backends/local-files.ts │ │
491
+ │ │ (atomic operations) │ │
492
+ │ └─────────────────────────────┘ │
493
+ └─────────────────────────────────────┘
494
+
495
+ │ SDK imports
496
+
497
+ ┌─────────────────────────────────────┐
498
+ │ @fractary/faber SDK │
499
+ │ FaberWorkflow │ StateManager │
500
+ └─────────────────────────────────────┘
501
+ ```
502
+
503
+ ## Concurrency Safety
504
+
505
+ The MCP server implements atomic operations for event ID generation to prevent race conditions when multiple agents emit events concurrently. This is achieved through:
506
+
507
+ 1. **Atomic File Operations**: Event IDs are generated using atomic file reads with retry logic
508
+ 2. **Directory-Level Locking**: State updates use temporary files with atomic renames
509
+ 3. **Safe Metadata Updates**: Run metadata is updated via atomic read-modify-write cycles
510
+
511
+ ## License
512
+
513
+ MIT
514
+
515
+ ## Contributing
516
+
517
+ See the main [FABER repository](https://github.com/fractary/faber) for contribution guidelines.
518
+
519
+ ## Support
520
+
521
+ - **Issues**: https://github.com/fractary/faber/issues
522
+ - **Documentation**: https://fractary.dev/docs/faber
523
+ - **MCP Protocol**: https://spec.modelcontextprotocol.io
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Local Files Backend for FABER Event Gateway
3
+ *
4
+ * Stores events as individual JSON files in the run's events directory.
5
+ * Uses atomic file operations for concurrent access safety.
6
+ */
7
+ import { ConsolidateResult, EmitEventResult, FaberEvent, GetRunResult, ListRunsResult } from "../types.js";
8
+ export declare class LocalFilesBackend {
9
+ private readonly basePath;
10
+ private readonly resolvedBasePath;
11
+ constructor(basePath: string);
12
+ /**
13
+ * Validate run_id format - strict validation to prevent edge cases
14
+ * - org and project must start and end with alphanumeric
15
+ * - uuid must be valid format
16
+ */
17
+ private validateRunId;
18
+ /**
19
+ * Get the directory path for a run with path traversal protection
20
+ */
21
+ private getRunDir;
22
+ /**
23
+ * Get the events directory for a run
24
+ */
25
+ private getEventsDir;
26
+ /**
27
+ * Generate ISO timestamp with milliseconds for consistency
28
+ */
29
+ private getTimestamp;
30
+ /**
31
+ * Sleep helper for retry backoff
32
+ */
33
+ private sleep;
34
+ /**
35
+ * Get next event ID atomically using atomic rename pattern
36
+ *
37
+ * This approach is more reliable than file locking:
38
+ * 1. Read current ID
39
+ * 2. Write new ID to temp file with random suffix
40
+ * 3. Atomically rename temp file to target
41
+ * 4. If rename fails due to race, retry with exponential backoff
42
+ */
43
+ private getNextEventId;
44
+ /**
45
+ * Update state.json atomically - CRITICAL operation
46
+ * Throws on failure to ensure state consistency
47
+ */
48
+ private updateState;
49
+ /**
50
+ * Emit a workflow event
51
+ */
52
+ emitEvent(eventData: Partial<FaberEvent>): Promise<EmitEventResult>;
53
+ /**
54
+ * Get run state and metadata
55
+ */
56
+ getRun(runId: string, includeEvents?: boolean): Promise<GetRunResult>;
57
+ /**
58
+ * Get events for a run with streaming support for large event sets
59
+ * Uses a generator pattern to avoid loading all events into memory
60
+ */
61
+ getEventsStream(runId: string): AsyncGenerator<FaberEvent>;
62
+ /**
63
+ * Get all events for a run (for backward compatibility)
64
+ * For large event sets, prefer getEventsStream
65
+ */
66
+ getEvents(runId: string): Promise<FaberEvent[]>;
67
+ /**
68
+ * List runs with optional filters
69
+ * Uses an index file for performance when available
70
+ */
71
+ listRuns(filters: {
72
+ work_id?: string;
73
+ status?: string;
74
+ org?: string;
75
+ project?: string;
76
+ limit?: number;
77
+ }): Promise<ListRunsResult>;
78
+ /**
79
+ * Consolidate events to JSONL format using streaming
80
+ * Avoids loading all events into memory
81
+ */
82
+ consolidateEvents(runId: string): Promise<ConsolidateResult>;
83
+ /**
84
+ * Update the runs index for faster listing
85
+ * Should be called after run completion
86
+ */
87
+ updateRunsIndex(): Promise<void>;
88
+ }
89
+ //# sourceMappingURL=local-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-files.d.ts","sourceRoot":"","sources":["../../src/backends/local-files.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EACL,iBAAiB,EACjB,eAAe,EAGf,UAAU,EACV,YAAY,EACZ,cAAc,EAIf,MAAM,aAAa,CAAC;AAsDrB,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAE9B,QAAQ,EAAE,MAAM;IAM5B;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAOrB;;OAEG;IACH,OAAO,CAAC,SAAS;IAYjB;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;OAEG;IACH,OAAO,CAAC,KAAK;IAIb;;;;;;;;OAQG;YACW,cAAc;IAsD5B;;;OAGG;IACH,OAAO,CAAC,WAAW;IAmCnB;;OAEG;IACG,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC;IAuGzE;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,UAAQ,GAAG,OAAO,CAAC,YAAY,CAAC;IAwDzE;;;OAGG;IACI,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC;IAgCjE;;;OAGG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAQrD;;;OAGG;IACG,QAAQ,CAAC,OAAO,EAAE;QACtB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,cAAc,CAAC;IAkI3B;;;OAGG;IACG,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAiElE;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;CAOvC"}