@lumenflow/mcp 2.11.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/LICENSE +190 -0
- package/dist/bin.d.ts +15 -0
- package/dist/bin.js +68 -0
- package/dist/cli-runner.d.ts +66 -0
- package/dist/cli-runner.js +94 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +40 -0
- package/dist/resources.d.ts +60 -0
- package/dist/resources.js +128 -0
- package/dist/server.d.ts +68 -0
- package/dist/server.js +190 -0
- package/dist/tools.d.ts +67 -0
- package/dist/tools.js +314 -0
- package/package.json +76 -0
package/dist/tools.js
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file tools.ts
|
|
3
|
+
* @description MCP tool implementations for LumenFlow operations
|
|
4
|
+
*
|
|
5
|
+
* WU-1412: Tools available: context_get, wu_list, wu_status, wu_create, wu_claim, wu_done, gates_run
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* - Read operations (context_get) use @lumenflow/core directly for context
|
|
9
|
+
* - All other operations shell out to CLI for consistency and safety
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { runCliCommand } from './cli-runner.js';
|
|
13
|
+
// Import core functions for context operations only
|
|
14
|
+
let coreModule = null;
|
|
15
|
+
async function getCore() {
|
|
16
|
+
if (!coreModule) {
|
|
17
|
+
coreModule = await import('@lumenflow/core');
|
|
18
|
+
}
|
|
19
|
+
return coreModule;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Error codes used by tool implementations
|
|
23
|
+
*/
|
|
24
|
+
const ErrorCodes = {
|
|
25
|
+
MISSING_PARAMETER: 'MISSING_PARAMETER',
|
|
26
|
+
CONTEXT_ERROR: 'CONTEXT_ERROR',
|
|
27
|
+
WU_LIST_ERROR: 'WU_LIST_ERROR',
|
|
28
|
+
WU_STATUS_ERROR: 'WU_STATUS_ERROR',
|
|
29
|
+
WU_CREATE_ERROR: 'WU_CREATE_ERROR',
|
|
30
|
+
WU_CLAIM_ERROR: 'WU_CLAIM_ERROR',
|
|
31
|
+
WU_DONE_ERROR: 'WU_DONE_ERROR',
|
|
32
|
+
WRONG_LOCATION: 'WRONG_LOCATION',
|
|
33
|
+
GATES_ERROR: 'GATES_ERROR',
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Error messages used by tool implementations
|
|
37
|
+
*/
|
|
38
|
+
const ErrorMessages = {
|
|
39
|
+
ID_REQUIRED: 'id is required',
|
|
40
|
+
LANE_REQUIRED: 'lane is required',
|
|
41
|
+
TITLE_REQUIRED: 'title is required',
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Create a successful tool result
|
|
45
|
+
*/
|
|
46
|
+
function success(data) {
|
|
47
|
+
return { success: true, data };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create an error tool result
|
|
51
|
+
*/
|
|
52
|
+
function error(message, code) {
|
|
53
|
+
return { success: false, error: { message, code } };
|
|
54
|
+
}
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Read Operations (via @lumenflow/core)
|
|
57
|
+
// ============================================================================
|
|
58
|
+
/**
|
|
59
|
+
* context_get - Get current WU context (location, git state, WU state)
|
|
60
|
+
*/
|
|
61
|
+
export const contextGetTool = {
|
|
62
|
+
name: 'context_get',
|
|
63
|
+
description: 'Get current LumenFlow context including location, git state, and active WU',
|
|
64
|
+
inputSchema: z.object({}).optional(),
|
|
65
|
+
async execute(_input, options) {
|
|
66
|
+
try {
|
|
67
|
+
const core = await getCore();
|
|
68
|
+
const context = await core.computeWuContext({
|
|
69
|
+
cwd: options?.projectRoot,
|
|
70
|
+
});
|
|
71
|
+
return success(context);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
return error(err instanceof Error ? err.message : String(err), ErrorCodes.CONTEXT_ERROR);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* wu_list - List all WUs with optional status filter
|
|
80
|
+
* Uses CLI shell-out for consistency with other tools
|
|
81
|
+
*/
|
|
82
|
+
export const wuListTool = {
|
|
83
|
+
name: 'wu_list',
|
|
84
|
+
description: 'List all Work Units (WUs) with optional status filter',
|
|
85
|
+
inputSchema: z.object({
|
|
86
|
+
status: z.enum(['ready', 'in_progress', 'blocked', 'waiting', 'done']).optional(),
|
|
87
|
+
lane: z.string().optional(),
|
|
88
|
+
}),
|
|
89
|
+
async execute(input, options) {
|
|
90
|
+
// Use spec:linter which validates and lists all WUs
|
|
91
|
+
const cliOptions = { projectRoot: options?.projectRoot };
|
|
92
|
+
// Shell out to get all WU YAMLs via validate --all
|
|
93
|
+
const result = await runCliCommand('wu:validate', ['--all', '--json'], cliOptions);
|
|
94
|
+
if (result.success) {
|
|
95
|
+
try {
|
|
96
|
+
const data = JSON.parse(result.stdout);
|
|
97
|
+
let wus = Array.isArray(data) ? data : data.wus || [];
|
|
98
|
+
// Apply filters
|
|
99
|
+
if (input.status) {
|
|
100
|
+
wus = wus.filter((wu) => wu.status === input.status);
|
|
101
|
+
}
|
|
102
|
+
if (input.lane) {
|
|
103
|
+
wus = wus.filter((wu) => wu.lane === input.lane);
|
|
104
|
+
}
|
|
105
|
+
return success(wus);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// If JSON parse fails, return raw output
|
|
109
|
+
return success({ message: result.stdout });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
return error(result.stderr || result.error?.message || 'wu_list failed', ErrorCodes.WU_LIST_ERROR);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* wu_status - Get status of a specific WU
|
|
119
|
+
* Uses CLI shell-out for consistency
|
|
120
|
+
*/
|
|
121
|
+
export const wuStatusTool = {
|
|
122
|
+
name: 'wu_status',
|
|
123
|
+
description: 'Get detailed status of a specific Work Unit',
|
|
124
|
+
inputSchema: z.object({
|
|
125
|
+
id: z.string().describe('WU ID (e.g., WU-1412)'),
|
|
126
|
+
}),
|
|
127
|
+
async execute(input, options) {
|
|
128
|
+
if (!input.id) {
|
|
129
|
+
return error(ErrorMessages.ID_REQUIRED, ErrorCodes.MISSING_PARAMETER);
|
|
130
|
+
}
|
|
131
|
+
const args = ['--id', input.id, '--json'];
|
|
132
|
+
const cliOptions = { projectRoot: options?.projectRoot };
|
|
133
|
+
const result = await runCliCommand('wu:status', args, cliOptions);
|
|
134
|
+
if (result.success) {
|
|
135
|
+
try {
|
|
136
|
+
const data = JSON.parse(result.stdout);
|
|
137
|
+
return success(data);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return success({ message: result.stdout });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
return error(result.stderr || result.error?.message || 'wu:status failed', ErrorCodes.WU_STATUS_ERROR);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
// ============================================================================
|
|
149
|
+
// Write Operations (via CLI shell-out)
|
|
150
|
+
// ============================================================================
|
|
151
|
+
/**
|
|
152
|
+
* wu_create - Create a new WU
|
|
153
|
+
*/
|
|
154
|
+
export const wuCreateTool = {
|
|
155
|
+
name: 'wu_create',
|
|
156
|
+
description: 'Create a new Work Unit specification',
|
|
157
|
+
inputSchema: z.object({
|
|
158
|
+
id: z.string().optional().describe('WU ID (auto-generated if omitted)'),
|
|
159
|
+
lane: z.string().describe('Lane (e.g., "Framework: CLI")'),
|
|
160
|
+
title: z.string().describe('WU title'),
|
|
161
|
+
description: z.string().optional().describe('Context: ... Problem: ... Solution: ...'),
|
|
162
|
+
acceptance: z.array(z.string()).optional().describe('Acceptance criteria'),
|
|
163
|
+
code_paths: z.array(z.string()).optional().describe('Code paths'),
|
|
164
|
+
exposure: z.enum(['ui', 'api', 'backend-only', 'documentation']).optional(),
|
|
165
|
+
}),
|
|
166
|
+
async execute(input, options) {
|
|
167
|
+
if (!input.lane) {
|
|
168
|
+
return error(ErrorMessages.LANE_REQUIRED, ErrorCodes.MISSING_PARAMETER);
|
|
169
|
+
}
|
|
170
|
+
if (!input.title) {
|
|
171
|
+
return error(ErrorMessages.TITLE_REQUIRED, ErrorCodes.MISSING_PARAMETER);
|
|
172
|
+
}
|
|
173
|
+
const args = ['--lane', input.lane, '--title', input.title];
|
|
174
|
+
if (input.id)
|
|
175
|
+
args.push('--id', input.id);
|
|
176
|
+
if (input.description)
|
|
177
|
+
args.push('--description', input.description);
|
|
178
|
+
if (input.acceptance) {
|
|
179
|
+
for (const criterion of input.acceptance) {
|
|
180
|
+
args.push('--acceptance', criterion);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (input.code_paths) {
|
|
184
|
+
for (const p of input.code_paths) {
|
|
185
|
+
args.push('--code-paths', p);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (input.exposure)
|
|
189
|
+
args.push('--exposure', input.exposure);
|
|
190
|
+
const cliOptions = { projectRoot: options?.projectRoot };
|
|
191
|
+
const result = await runCliCommand('wu:create', args, cliOptions);
|
|
192
|
+
if (result.success) {
|
|
193
|
+
return success({ message: result.stdout || 'WU created successfully' });
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
return error(result.stderr || result.error?.message || 'wu:create failed', ErrorCodes.WU_CREATE_ERROR);
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* wu_claim - Claim a WU and create worktree
|
|
202
|
+
*/
|
|
203
|
+
export const wuClaimTool = {
|
|
204
|
+
name: 'wu_claim',
|
|
205
|
+
description: 'Claim a Work Unit and create worktree for implementation',
|
|
206
|
+
inputSchema: z.object({
|
|
207
|
+
id: z.string().describe('WU ID to claim'),
|
|
208
|
+
lane: z.string().describe('Lane for the WU'),
|
|
209
|
+
}),
|
|
210
|
+
async execute(input, options) {
|
|
211
|
+
if (!input.id) {
|
|
212
|
+
return error(ErrorMessages.ID_REQUIRED, ErrorCodes.MISSING_PARAMETER);
|
|
213
|
+
}
|
|
214
|
+
if (!input.lane) {
|
|
215
|
+
return error(ErrorMessages.LANE_REQUIRED, ErrorCodes.MISSING_PARAMETER);
|
|
216
|
+
}
|
|
217
|
+
const args = ['--id', input.id, '--lane', input.lane];
|
|
218
|
+
const cliOptions = { projectRoot: options?.projectRoot };
|
|
219
|
+
const result = await runCliCommand('wu:claim', args, cliOptions);
|
|
220
|
+
if (result.success) {
|
|
221
|
+
return success({ message: result.stdout || 'WU claimed successfully' });
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
return error(result.stderr || result.error?.message || 'wu:claim failed', ErrorCodes.WU_CLAIM_ERROR);
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* wu_done - Complete a WU (must be run from main checkout)
|
|
230
|
+
*/
|
|
231
|
+
export const wuDoneTool = {
|
|
232
|
+
name: 'wu_done',
|
|
233
|
+
description: 'Complete a Work Unit (merge, stamp, cleanup). MUST be run from main checkout.',
|
|
234
|
+
inputSchema: z.object({
|
|
235
|
+
id: z.string().describe('WU ID to complete'),
|
|
236
|
+
skip_gates: z.boolean().optional().describe('Skip gates (requires reason)'),
|
|
237
|
+
reason: z.string().optional().describe('Reason for skipping gates'),
|
|
238
|
+
fix_wu: z.string().optional().describe('WU ID that will fix the skipped issue'),
|
|
239
|
+
}),
|
|
240
|
+
async execute(input, options) {
|
|
241
|
+
if (!input.id) {
|
|
242
|
+
return error(ErrorMessages.ID_REQUIRED, ErrorCodes.MISSING_PARAMETER);
|
|
243
|
+
}
|
|
244
|
+
// Fail fast if not on main checkout (AC: wu_done fails fast if not on main checkout)
|
|
245
|
+
try {
|
|
246
|
+
const core = await getCore();
|
|
247
|
+
const context = await core.computeWuContext({
|
|
248
|
+
cwd: options?.projectRoot,
|
|
249
|
+
});
|
|
250
|
+
if (context.location.type === 'worktree') {
|
|
251
|
+
return error('wu_done must be run from main checkout, not from a worktree. ' +
|
|
252
|
+
'Run "pnpm wu:prep" first from the worktree, then cd to main and run wu:done.', ErrorCodes.WRONG_LOCATION);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// If we can't determine context, proceed anyway - CLI will validate
|
|
257
|
+
}
|
|
258
|
+
const args = ['--id', input.id];
|
|
259
|
+
if (input.skip_gates) {
|
|
260
|
+
args.push('--skip-gates');
|
|
261
|
+
if (input.reason)
|
|
262
|
+
args.push('--reason', input.reason);
|
|
263
|
+
if (input.fix_wu)
|
|
264
|
+
args.push('--fix-wu', input.fix_wu);
|
|
265
|
+
}
|
|
266
|
+
const cliOptions = { projectRoot: options?.projectRoot };
|
|
267
|
+
const result = await runCliCommand('wu:done', args, cliOptions);
|
|
268
|
+
if (result.success) {
|
|
269
|
+
return success({ message: result.stdout || 'WU completed successfully' });
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
return error(result.stderr || result.error?.message || 'wu:done failed', ErrorCodes.WU_DONE_ERROR);
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
/**
|
|
277
|
+
* gates_run - Run quality gates
|
|
278
|
+
*/
|
|
279
|
+
export const gatesRunTool = {
|
|
280
|
+
name: 'gates_run',
|
|
281
|
+
description: 'Run LumenFlow quality gates (lint, typecheck, tests)',
|
|
282
|
+
inputSchema: z.object({
|
|
283
|
+
docs_only: z.boolean().optional().describe('Run docs-only gates (skip lint/typecheck/tests)'),
|
|
284
|
+
}),
|
|
285
|
+
async execute(input, options) {
|
|
286
|
+
const args = [];
|
|
287
|
+
if (input.docs_only) {
|
|
288
|
+
args.push('--docs-only');
|
|
289
|
+
}
|
|
290
|
+
const cliOptions = {
|
|
291
|
+
projectRoot: options?.projectRoot,
|
|
292
|
+
timeout: 600000, // 10 minutes for gates
|
|
293
|
+
};
|
|
294
|
+
const result = await runCliCommand('gates', args, cliOptions);
|
|
295
|
+
if (result.success) {
|
|
296
|
+
return success({ message: result.stdout || 'All gates passed' });
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
return error(result.stderr || result.error?.message || 'Gates failed', ErrorCodes.GATES_ERROR);
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* All available tools
|
|
305
|
+
*/
|
|
306
|
+
export const allTools = [
|
|
307
|
+
contextGetTool,
|
|
308
|
+
wuListTool,
|
|
309
|
+
wuStatusTool,
|
|
310
|
+
wuCreateTool,
|
|
311
|
+
wuClaimTool,
|
|
312
|
+
wuDoneTool,
|
|
313
|
+
gatesRunTool,
|
|
314
|
+
];
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lumenflow/mcp",
|
|
3
|
+
"version": "2.11.0",
|
|
4
|
+
"description": "MCP stdio server for LumenFlow workflow framework",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"lumenflow",
|
|
7
|
+
"mcp",
|
|
8
|
+
"model-context-protocol",
|
|
9
|
+
"workflow",
|
|
10
|
+
"wu",
|
|
11
|
+
"work-unit"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/hellmai/os",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/hellmai/os.git",
|
|
17
|
+
"directory": "packages/@lumenflow/mcp"
|
|
18
|
+
},
|
|
19
|
+
"license": "Apache-2.0",
|
|
20
|
+
"author": {
|
|
21
|
+
"name": "HellmAI",
|
|
22
|
+
"url": "https://hellm.ai"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./server": {
|
|
33
|
+
"types": "./dist/server.d.ts",
|
|
34
|
+
"import": "./dist/server.js"
|
|
35
|
+
},
|
|
36
|
+
"./tools": {
|
|
37
|
+
"types": "./dist/tools.d.ts",
|
|
38
|
+
"import": "./dist/tools.js"
|
|
39
|
+
},
|
|
40
|
+
"./cli-runner": {
|
|
41
|
+
"types": "./dist/cli-runner.d.ts",
|
|
42
|
+
"import": "./dist/cli-runner.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"bin": {
|
|
46
|
+
"lumenflow-mcp": "./dist/bin.js"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist",
|
|
50
|
+
"LICENSE",
|
|
51
|
+
"README.md"
|
|
52
|
+
],
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
55
|
+
"zod": "^4.3.5",
|
|
56
|
+
"@lumenflow/core": "2.11.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@vitest/coverage-v8": "^4.0.17",
|
|
60
|
+
"typescript": "^5.9.3",
|
|
61
|
+
"vitest": "^4.0.17"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=22"
|
|
65
|
+
},
|
|
66
|
+
"publishConfig": {
|
|
67
|
+
"access": "public"
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"build": "tsc",
|
|
71
|
+
"build:dist": "tsc -p tsconfig.build.json",
|
|
72
|
+
"pack:dist": "pnpm pack",
|
|
73
|
+
"clean": "rm -rf dist *.tgz",
|
|
74
|
+
"test": "vitest run"
|
|
75
|
+
}
|
|
76
|
+
}
|