@wrongstack/requirement-intake-mcp 0.299.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ECOSTACK TECHNOLOGY OÜ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @wrongstack/requirement-intake-mcp
2
+
3
+ WrongStack Requirements Intake as a project-scoped MCP server. Agents (Claude
4
+ Code, Codex, Cursor, custom MCP clients) can list intake records and file +
5
+ submit new requirement intakes directly into the project's intake store.
6
+
7
+ Mirrors the `@wrongstack/kanban-mcp` pattern: a standalone server with a
8
+ read-only default and an explicit `--writable` tier.
9
+
10
+ ## Tools
11
+
12
+ | Tool | Tier | Purpose |
13
+ |---|---|---|
14
+ | `requirement_intake_list` | read (always) | List intake records for the project, newest first, optional `statuses` filter |
15
+ | `requirement_intake_submit` | writable (`--writable`) | File + submit an intake record from the `request` text (preserved verbatim) |
16
+
17
+ `requirement_intake_submit` arguments: `request` (required), `title`,
18
+ `requestType` (feature/bug_fix/refactor/… — unknown values normalize to
19
+ `other`/`unspecified`), `priority`, `idempotencyKey` (safe retries).
20
+
21
+ Records are stored under the project state dir
22
+ (`~/.wrongstack/projects/<slug>/requirement-intakes`) and are the same records
23
+ served by the WebUI REST API and the CLI `/intake` command. The MCP transport
24
+ is the authorization boundary; the service runs with an allow-all authorizer
25
+ inside it.
26
+
27
+ ## Usage
28
+
29
+ ```bash
30
+ # stdio (default) — read-only
31
+ wstack-requirement-intake-mcp --project-root /path/to/project
32
+
33
+ # expose intake filing
34
+ wstack-requirement-intake-mcp --project-root /path/to/project --writable
35
+
36
+ # HTTP transport with token auth
37
+ WRONGSTACK_MCP_TOKEN=secret wstack-requirement-intake-mcp \
38
+ --project-root /path/to/project --http --writable
39
+ ```
40
+
41
+ Project identity comes from `.wrongstack/project.json`
42
+ (`readProjectIdentity`). `requirement_intake_submit` creates it when missing;
43
+ `requirement_intake_list` requires it (run `wstack init` first).
44
+
45
+ ## Development
46
+
47
+ ```bash
48
+ pnpm exec vitest run packages/requirement-intake-mcp/tests
49
+ pnpm --filter @wrongstack/requirement-intake-mcp build
50
+ ```
51
+
52
+ ## Code references
53
+
54
+ - `src/adapter.ts` — `createRequirementIntakeMcpToolHost` / `...Server`
55
+ - `src/policy.ts` — read/writable capability tiers
56
+ - `src/cli.ts` — `wstack-requirement-intake-mcp` entry
@@ -0,0 +1,19 @@
1
+ import { MCPServer, type MCPServerToolHost } from '@wrongstack/mcp';
2
+ import { RequirementIntakeService } from '@wrongstack/requirement-intake';
3
+ import { type RequirementIntakeMcpPolicyOptions } from './policy.js';
4
+ export interface RequirementIntakeMcpDependencies {
5
+ /** Pre-built intake service (tests/embeds). Defaults to a fresh per-project service. */
6
+ service?: RequirementIntakeService | undefined;
7
+ /**
8
+ * Resolve the canonical project id. Defaults to reading `.wrongstack/project.json`
9
+ * (`readProjectIdentity`); when missing, `createIfMissing` (submit) creates it.
10
+ */
11
+ resolveProjectId?: ((createIfMissing: boolean) => Promise<string>) | undefined;
12
+ }
13
+ export interface RequirementIntakeMcpToolHostOptions extends RequirementIntakeMcpPolicyOptions {
14
+ actor?: string | undefined;
15
+ dependencies?: RequirementIntakeMcpDependencies | undefined;
16
+ }
17
+ export declare function createRequirementIntakeMcpToolHost(projectRoot: string, opts?: RequirementIntakeMcpToolHostOptions): MCPServerToolHost;
18
+ export declare function createRequirementIntakeMcpServer(projectRoot: string, opts?: RequirementIntakeMcpToolHostOptions): MCPServer;
19
+ //# sourceMappingURL=adapter.d.ts.map
package/dist/cli.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ export interface ParsedArgs {
3
+ projectRoot: string;
4
+ transport: 'stdio' | 'http';
5
+ httpPort: number;
6
+ httpHost: string;
7
+ httpToken?: string | undefined;
8
+ writable: boolean;
9
+ actor?: string | undefined;
10
+ help: boolean;
11
+ }
12
+ export declare function printHelp(stdout: NodeJS.WriteStream): void;
13
+ export declare function parseArgs(argv: readonly string[], env?: NodeJS.ProcessEnv): ParsedArgs;
14
+ export declare function main(argv?: string[]): Promise<number>;
15
+ //# sourceMappingURL=cli.d.ts.map
package/dist/cli.js ADDED
@@ -0,0 +1,409 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { realpathSync } from "node:fs";
5
+ import * as path from "node:path";
6
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
7
+ import { canonicalProjectRoot } from "@wrongstack/core/utils";
8
+ import { serveHttp, serveStdio } from "@wrongstack/mcp";
9
+
10
+ // src/adapter.ts
11
+ import {
12
+ ensureProjectIdentity,
13
+ readProjectIdentity,
14
+ resolveWstackPaths
15
+ } from "@wrongstack/core/utils";
16
+ import {
17
+ MCPServer
18
+ } from "@wrongstack/mcp";
19
+ import {
20
+ AllowAllIntakeAuthorizer,
21
+ INTAKE_PRIORITIES,
22
+ INTAKE_STATUSES,
23
+ RequirementIntakeService,
24
+ RequirementIntakeStore
25
+ } from "@wrongstack/requirement-intake";
26
+
27
+ // src/policy.ts
28
+ function selectRequirementIntakeTools(opts = {}) {
29
+ const tools = [{ name: "requirement_intake_list" }];
30
+ if (opts.writable === true) {
31
+ tools.push({ name: "requirement_intake_submit" });
32
+ }
33
+ return tools;
34
+ }
35
+
36
+ // src/version.ts
37
+ import { readFileSync } from "node:fs";
38
+ import { dirname, resolve } from "node:path";
39
+ import { fileURLToPath } from "node:url";
40
+ var here = dirname(fileURLToPath(import.meta.url));
41
+ var packagePath = resolve(here, "..", "package.json");
42
+ var cached;
43
+ function readServerInfo() {
44
+ if (cached) return cached;
45
+ try {
46
+ const pkg = JSON.parse(readFileSync(packagePath, "utf8"));
47
+ cached = {
48
+ name: pkg.name ?? "@wrongstack/requirement-intake-mcp",
49
+ version: pkg.version ?? "0.0.0"
50
+ };
51
+ } catch {
52
+ cached = { name: "@wrongstack/requirement-intake-mcp", version: "0.0.0" };
53
+ }
54
+ return cached;
55
+ }
56
+ var SERVER_INFO = readServerInfo();
57
+
58
+ // src/adapter.ts
59
+ var SUBMIT_SCHEMA = {
60
+ type: "object",
61
+ properties: {
62
+ request: {
63
+ type: "string",
64
+ description: "The exact software development request to record \u2014 a feature, bug fix, refactor, UI/API/infra change, migration, documentation, etc. Preserved verbatim."
65
+ },
66
+ title: {
67
+ type: "string",
68
+ description: "Optional short title. Defaults to a deterministic title from the request."
69
+ },
70
+ requestType: {
71
+ type: "string",
72
+ enum: [
73
+ "feature",
74
+ "bug_fix",
75
+ "refactor",
76
+ "performance",
77
+ "security",
78
+ "ui_change",
79
+ "api_change",
80
+ "infrastructure",
81
+ "migration",
82
+ "testing",
83
+ "documentation",
84
+ "maintenance",
85
+ "other",
86
+ "unspecified"
87
+ ],
88
+ description: "Request type hint. Unknown values normalize to other/unspecified."
89
+ },
90
+ priority: {
91
+ type: "string",
92
+ enum: [...INTAKE_PRIORITIES],
93
+ description: "Desired priority."
94
+ },
95
+ idempotencyKey: {
96
+ type: "string",
97
+ description: "Optional key making create idempotent \u2014 retries return the existing record."
98
+ }
99
+ },
100
+ required: ["request"],
101
+ additionalProperties: false
102
+ };
103
+ var LIST_SCHEMA = {
104
+ type: "object",
105
+ properties: {
106
+ statuses: {
107
+ type: "array",
108
+ items: { type: "string", enum: [...INTAKE_STATUSES] },
109
+ description: "Optional status filter (draft, collecting_information, submitted, cancelled, archived)."
110
+ }
111
+ },
112
+ additionalProperties: false
113
+ };
114
+ var TOOL_DESCRIPTIONS = {
115
+ requirement_intake_list: "List requirement intake records for the project, newest first, optionally filtered by status.",
116
+ requirement_intake_submit: "File and submit a requirement intake record from the given request text. Requires server --writable."
117
+ };
118
+ function toolDescriptor(name) {
119
+ return {
120
+ name,
121
+ description: TOOL_DESCRIPTIONS[name],
122
+ inputSchema: name === "requirement_intake_submit" ? SUBMIT_SCHEMA : LIST_SCHEMA
123
+ };
124
+ }
125
+ function intakeContext(projectId, actor) {
126
+ return { id: actor, type: "automation", projectId };
127
+ }
128
+ async function defaultResolveProjectId(projectRoot, createIfMissing) {
129
+ const existing = await readProjectIdentity(projectRoot);
130
+ if (existing) return existing.projectId;
131
+ if (!createIfMissing) {
132
+ throw new Error(
133
+ "No WrongStack project identity found \u2014 run `wstack init` (or file an intake first) to create it"
134
+ );
135
+ }
136
+ return (await ensureProjectIdentity(projectRoot)).identity.projectId;
137
+ }
138
+ function createRequirementIntakeMcpToolHost(projectRoot, opts = {}) {
139
+ const policy = selectRequirementIntakeTools(opts);
140
+ const allowed = new Set(policy.map((entry) => entry.name));
141
+ const actor = opts.actor?.trim() || "external-intake-mcp";
142
+ const service = opts.dependencies?.service ?? new RequirementIntakeService({
143
+ store: new RequirementIntakeStore({
144
+ baseDir: resolveWstackPaths({ projectRoot }).projectRequirementIntakes
145
+ }),
146
+ authorizer: new AllowAllIntakeAuthorizer()
147
+ });
148
+ const resolveProjectId = opts.dependencies?.resolveProjectId ?? ((createIfMissing) => defaultResolveProjectId(projectRoot, createIfMissing));
149
+ return {
150
+ listTools() {
151
+ return policy.map((entry) => toolDescriptor(entry.name));
152
+ },
153
+ async callTool(name, args) {
154
+ if (!allowed.has(name)) {
155
+ return {
156
+ content: `Tool "${name}" is not exposed by this Requirements Intake MCP server`,
157
+ isError: true
158
+ };
159
+ }
160
+ try {
161
+ if (name === "requirement_intake_submit") {
162
+ return await submitIntake(args);
163
+ }
164
+ return await listIntakes(args);
165
+ } catch (error) {
166
+ return {
167
+ content: error instanceof Error ? error.message : String(error),
168
+ isError: true
169
+ };
170
+ }
171
+ }
172
+ };
173
+ async function submitIntake(args) {
174
+ const request = args["request"];
175
+ if (typeof request !== "string" || request.trim().length === 0) {
176
+ return {
177
+ content: 'requirement_intake_submit requires a non-blank "request" string',
178
+ isError: true
179
+ };
180
+ }
181
+ const projectId = await resolveProjectId(true);
182
+ const ctx = intakeContext(projectId, actor);
183
+ const result = await service.createIntake(
184
+ {
185
+ projectId,
186
+ originalRequest: request,
187
+ requestedBy: actor,
188
+ ...typeof args["title"] === "string" ? { title: args["title"] } : {},
189
+ ...typeof args["requestType"] === "string" ? { requestType: args["requestType"] } : {},
190
+ ...typeof args["priority"] === "string" ? { priority: args["priority"] } : {},
191
+ ...typeof args["idempotencyKey"] === "string" ? { idempotencyKey: args["idempotencyKey"] } : {}
192
+ },
193
+ ctx
194
+ );
195
+ const submitted = await service.submitIntake(result.record.id, ctx);
196
+ return {
197
+ content: {
198
+ intakeId: submitted.record.id,
199
+ title: submitted.record.title,
200
+ requestType: submitted.record.requestType,
201
+ status: submitted.record.status,
202
+ projectId,
203
+ created: result.created,
204
+ idempotent: submitted.idempotent
205
+ },
206
+ isError: false
207
+ };
208
+ }
209
+ async function listIntakes(args) {
210
+ const projectId = await resolveProjectId(false);
211
+ const ctx = intakeContext(projectId, actor);
212
+ const statuses = filterStatuses(args["statuses"]);
213
+ const records = await service.listIntakes(projectId, ctx, statuses ? { statuses } : void 0);
214
+ return {
215
+ content: {
216
+ projectId,
217
+ count: records.length,
218
+ intakes: records.map((record) => ({
219
+ id: record.id,
220
+ title: record.title,
221
+ requestType: record.requestType,
222
+ status: record.status,
223
+ priority: record.priority,
224
+ updatedAt: record.updatedAt,
225
+ createdAt: record.createdAt
226
+ }))
227
+ },
228
+ isError: false
229
+ };
230
+ }
231
+ }
232
+ function filterStatuses(value) {
233
+ if (!Array.isArray(value)) return void 0;
234
+ const known = value.filter(
235
+ (item) => INTAKE_STATUSES.includes(String(item))
236
+ );
237
+ return known.length > 0 ? known : void 0;
238
+ }
239
+ function createRequirementIntakeMcpServer(projectRoot, opts = {}) {
240
+ return new MCPServer({
241
+ host: createRequirementIntakeMcpToolHost(projectRoot, opts),
242
+ serverInfo: { name: "wrongstack-requirement-intake-mcp", version: SERVER_INFO.version },
243
+ prompts: [
244
+ {
245
+ name: "file-requirement-intake",
246
+ title: "File a WrongStack requirement intake",
247
+ description: "Record and submit an unstructured software development request as a structured intake record.",
248
+ arguments: [{ name: "request", description: "The exact request text", required: true }],
249
+ template: "Use requirement_intake_submit to record and submit the request {{request}} verbatim. If a matching intake record may already exist, list records first and reuse the idempotency key pattern."
250
+ }
251
+ ]
252
+ });
253
+ }
254
+
255
+ // src/cli.ts
256
+ function printHelp(stdout) {
257
+ stdout.write(
258
+ [
259
+ `${SERVER_INFO.name} v${SERVER_INFO.version} \u2014 WrongStack Requirements Intake MCP server`,
260
+ "",
261
+ "Usage:",
262
+ ` ${SERVER_INFO.name} --project-root <path> [options]`,
263
+ "",
264
+ "Options:",
265
+ " --project-root <path> Project whose requirement intakes should be served (required).",
266
+ " --stdio Use stdio transport (default).",
267
+ " --http Use HTTP transport.",
268
+ " --port <n> HTTP port (default 0 = ephemeral).",
269
+ " --host <h> HTTP bind host (default 127.0.0.1).",
270
+ " --token <t> Bearer token. Required for a non-loopback HTTP bind.",
271
+ " Prefer WRONGSTACK_MCP_TOKEN \u2014 a command line is readable",
272
+ " by other local processes (WS-064).",
273
+ " --actor <id> Actor id recorded as the intake requester.",
274
+ " --writable Expose requirement_intake_submit (filing records).",
275
+ " -h, --help Show this message."
276
+ ].join("\n") + "\n"
277
+ );
278
+ }
279
+ var HTTP_TOKEN_ENV = "WRONGSTACK_MCP_TOKEN";
280
+ function parseArgs(argv, env = process.env) {
281
+ const parsed = {
282
+ projectRoot: "",
283
+ transport: "stdio",
284
+ httpPort: 0,
285
+ httpHost: "127.0.0.1",
286
+ writable: false,
287
+ help: false
288
+ };
289
+ for (let index = 0; index < argv.length; index++) {
290
+ const arg = argv[index];
291
+ switch (arg) {
292
+ case "--project-root":
293
+ parsed.projectRoot = path.resolve(argv[++index] ?? "");
294
+ break;
295
+ case "--stdio":
296
+ parsed.transport = "stdio";
297
+ break;
298
+ case "--http":
299
+ parsed.transport = "http";
300
+ break;
301
+ case "--port":
302
+ parsed.httpPort = Number(argv[++index] ?? "") || 0;
303
+ break;
304
+ case "--host":
305
+ parsed.httpHost = argv[++index] ?? "127.0.0.1";
306
+ break;
307
+ case "--token":
308
+ parsed.httpToken = argv[++index];
309
+ break;
310
+ case "--actor":
311
+ parsed.actor = argv[++index];
312
+ break;
313
+ case "--writable":
314
+ parsed.writable = true;
315
+ break;
316
+ case "-h":
317
+ case "--help":
318
+ parsed.help = true;
319
+ break;
320
+ default:
321
+ break;
322
+ }
323
+ }
324
+ if (parsed.httpToken === void 0) {
325
+ const fromEnv = env[HTTP_TOKEN_ENV]?.trim();
326
+ if (fromEnv) parsed.httpToken = fromEnv;
327
+ } else if (parsed.httpToken.length > 0) {
328
+ console.warn(
329
+ `[mcp] --token puts the auth token in this process's command line, which other local processes can read. Prefer ${HTTP_TOKEN_ENV}.`
330
+ );
331
+ }
332
+ return parsed;
333
+ }
334
+ async function main(argv = process.argv.slice(2)) {
335
+ const args = parseArgs(argv);
336
+ if (args.help) {
337
+ printHelp(process.stdout);
338
+ return 0;
339
+ }
340
+ if (!args.projectRoot) {
341
+ process.stderr.write(`${SERVER_INFO.name}: --project-root is required
342
+ `);
343
+ printHelp(process.stderr);
344
+ return 2;
345
+ }
346
+ const projectRoot = canonicalProjectRoot(args.projectRoot);
347
+ const server = createRequirementIntakeMcpServer(projectRoot, {
348
+ writable: args.writable,
349
+ ...args.actor ? { actor: args.actor } : {}
350
+ });
351
+ const policyText = `writable=${String(args.writable)}`;
352
+ if (args.transport === "http") {
353
+ const handle2 = await serveHttp(server, {
354
+ port: args.httpPort,
355
+ host: args.httpHost,
356
+ ...args.httpToken ? { token: args.httpToken } : {},
357
+ logger: { warn: (message) => process.stderr.write(`[requirement-intake-mcp] ${message}
358
+ `) }
359
+ });
360
+ process.stderr.write(
361
+ `${SERVER_INFO.name}: ready at ${handle2.url} \u2014 projectRoot=${projectRoot} transport=http ${policyText}${args.httpToken ? " [token auth]" : ""}
362
+ `
363
+ );
364
+ await new Promise((resolve3) => {
365
+ process.once("SIGINT", resolve3);
366
+ process.once("SIGTERM", resolve3);
367
+ });
368
+ await handle2.close();
369
+ return 0;
370
+ }
371
+ const handle = serveStdio(server);
372
+ process.stderr.write(
373
+ `${SERVER_INFO.name}: ready on stdio \u2014 projectRoot=${projectRoot} transport=stdio ${policyText}
374
+ `
375
+ );
376
+ await handle.done;
377
+ return 0;
378
+ }
379
+ function isMainModule() {
380
+ const entry = process.argv[1];
381
+ if (!entry) return false;
382
+ const self = fileURLToPath2(import.meta.url);
383
+ if (path.resolve(entry) === self) return true;
384
+ try {
385
+ return realpathSync(entry) === realpathSync(self);
386
+ } catch {
387
+ return false;
388
+ }
389
+ }
390
+ if (isMainModule()) {
391
+ main().then(
392
+ (code) => {
393
+ process.exitCode = code;
394
+ },
395
+ (error) => {
396
+ process.stderr.write(`${SERVER_INFO.name}: unexpected error
397
+ `);
398
+ process.stderr.write(error instanceof Error ? error.stack ?? error.message : String(error));
399
+ process.stderr.write("\n");
400
+ process.exitCode = 1;
401
+ }
402
+ );
403
+ }
404
+ export {
405
+ main,
406
+ parseArgs,
407
+ printHelp
408
+ };
409
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,4 @@
1
+ export { createRequirementIntakeMcpServer, createRequirementIntakeMcpToolHost, type RequirementIntakeMcpDependencies, type RequirementIntakeMcpToolHostOptions, } from './adapter.js';
2
+ export { REQUIREMENT_INTAKE_READ_TOOLS, REQUIREMENT_INTAKE_WRITE_TOOLS, type RequirementIntakeMcpPolicyOptions, type RequirementIntakeMcpToolName, type RequirementIntakeMcpToolPolicy, selectRequirementIntakeTools, } from './policy.js';
3
+ export { SERVER_INFO } from './version.js';
4
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,255 @@
1
+ // src/adapter.ts
2
+ import {
3
+ ensureProjectIdentity,
4
+ readProjectIdentity,
5
+ resolveWstackPaths
6
+ } from "@wrongstack/core/utils";
7
+ import {
8
+ MCPServer
9
+ } from "@wrongstack/mcp";
10
+ import {
11
+ AllowAllIntakeAuthorizer,
12
+ INTAKE_PRIORITIES,
13
+ INTAKE_STATUSES,
14
+ RequirementIntakeService,
15
+ RequirementIntakeStore
16
+ } from "@wrongstack/requirement-intake";
17
+
18
+ // src/policy.ts
19
+ var REQUIREMENT_INTAKE_READ_TOOLS = ["requirement_intake_list"];
20
+ var REQUIREMENT_INTAKE_WRITE_TOOLS = ["requirement_intake_submit"];
21
+ function selectRequirementIntakeTools(opts = {}) {
22
+ const tools = [{ name: "requirement_intake_list" }];
23
+ if (opts.writable === true) {
24
+ tools.push({ name: "requirement_intake_submit" });
25
+ }
26
+ return tools;
27
+ }
28
+
29
+ // src/version.ts
30
+ import { readFileSync } from "node:fs";
31
+ import { dirname, resolve } from "node:path";
32
+ import { fileURLToPath } from "node:url";
33
+ var here = dirname(fileURLToPath(import.meta.url));
34
+ var packagePath = resolve(here, "..", "package.json");
35
+ var cached;
36
+ function readServerInfo() {
37
+ if (cached) return cached;
38
+ try {
39
+ const pkg = JSON.parse(readFileSync(packagePath, "utf8"));
40
+ cached = {
41
+ name: pkg.name ?? "@wrongstack/requirement-intake-mcp",
42
+ version: pkg.version ?? "0.0.0"
43
+ };
44
+ } catch {
45
+ cached = { name: "@wrongstack/requirement-intake-mcp", version: "0.0.0" };
46
+ }
47
+ return cached;
48
+ }
49
+ var SERVER_INFO = readServerInfo();
50
+
51
+ // src/adapter.ts
52
+ var SUBMIT_SCHEMA = {
53
+ type: "object",
54
+ properties: {
55
+ request: {
56
+ type: "string",
57
+ description: "The exact software development request to record \u2014 a feature, bug fix, refactor, UI/API/infra change, migration, documentation, etc. Preserved verbatim."
58
+ },
59
+ title: {
60
+ type: "string",
61
+ description: "Optional short title. Defaults to a deterministic title from the request."
62
+ },
63
+ requestType: {
64
+ type: "string",
65
+ enum: [
66
+ "feature",
67
+ "bug_fix",
68
+ "refactor",
69
+ "performance",
70
+ "security",
71
+ "ui_change",
72
+ "api_change",
73
+ "infrastructure",
74
+ "migration",
75
+ "testing",
76
+ "documentation",
77
+ "maintenance",
78
+ "other",
79
+ "unspecified"
80
+ ],
81
+ description: "Request type hint. Unknown values normalize to other/unspecified."
82
+ },
83
+ priority: {
84
+ type: "string",
85
+ enum: [...INTAKE_PRIORITIES],
86
+ description: "Desired priority."
87
+ },
88
+ idempotencyKey: {
89
+ type: "string",
90
+ description: "Optional key making create idempotent \u2014 retries return the existing record."
91
+ }
92
+ },
93
+ required: ["request"],
94
+ additionalProperties: false
95
+ };
96
+ var LIST_SCHEMA = {
97
+ type: "object",
98
+ properties: {
99
+ statuses: {
100
+ type: "array",
101
+ items: { type: "string", enum: [...INTAKE_STATUSES] },
102
+ description: "Optional status filter (draft, collecting_information, submitted, cancelled, archived)."
103
+ }
104
+ },
105
+ additionalProperties: false
106
+ };
107
+ var TOOL_DESCRIPTIONS = {
108
+ requirement_intake_list: "List requirement intake records for the project, newest first, optionally filtered by status.",
109
+ requirement_intake_submit: "File and submit a requirement intake record from the given request text. Requires server --writable."
110
+ };
111
+ function toolDescriptor(name) {
112
+ return {
113
+ name,
114
+ description: TOOL_DESCRIPTIONS[name],
115
+ inputSchema: name === "requirement_intake_submit" ? SUBMIT_SCHEMA : LIST_SCHEMA
116
+ };
117
+ }
118
+ function intakeContext(projectId, actor) {
119
+ return { id: actor, type: "automation", projectId };
120
+ }
121
+ async function defaultResolveProjectId(projectRoot, createIfMissing) {
122
+ const existing = await readProjectIdentity(projectRoot);
123
+ if (existing) return existing.projectId;
124
+ if (!createIfMissing) {
125
+ throw new Error(
126
+ "No WrongStack project identity found \u2014 run `wstack init` (or file an intake first) to create it"
127
+ );
128
+ }
129
+ return (await ensureProjectIdentity(projectRoot)).identity.projectId;
130
+ }
131
+ function createRequirementIntakeMcpToolHost(projectRoot, opts = {}) {
132
+ const policy = selectRequirementIntakeTools(opts);
133
+ const allowed = new Set(policy.map((entry) => entry.name));
134
+ const actor = opts.actor?.trim() || "external-intake-mcp";
135
+ const service = opts.dependencies?.service ?? new RequirementIntakeService({
136
+ store: new RequirementIntakeStore({
137
+ baseDir: resolveWstackPaths({ projectRoot }).projectRequirementIntakes
138
+ }),
139
+ authorizer: new AllowAllIntakeAuthorizer()
140
+ });
141
+ const resolveProjectId = opts.dependencies?.resolveProjectId ?? ((createIfMissing) => defaultResolveProjectId(projectRoot, createIfMissing));
142
+ return {
143
+ listTools() {
144
+ return policy.map((entry) => toolDescriptor(entry.name));
145
+ },
146
+ async callTool(name, args) {
147
+ if (!allowed.has(name)) {
148
+ return {
149
+ content: `Tool "${name}" is not exposed by this Requirements Intake MCP server`,
150
+ isError: true
151
+ };
152
+ }
153
+ try {
154
+ if (name === "requirement_intake_submit") {
155
+ return await submitIntake(args);
156
+ }
157
+ return await listIntakes(args);
158
+ } catch (error) {
159
+ return {
160
+ content: error instanceof Error ? error.message : String(error),
161
+ isError: true
162
+ };
163
+ }
164
+ }
165
+ };
166
+ async function submitIntake(args) {
167
+ const request = args["request"];
168
+ if (typeof request !== "string" || request.trim().length === 0) {
169
+ return {
170
+ content: 'requirement_intake_submit requires a non-blank "request" string',
171
+ isError: true
172
+ };
173
+ }
174
+ const projectId = await resolveProjectId(true);
175
+ const ctx = intakeContext(projectId, actor);
176
+ const result = await service.createIntake(
177
+ {
178
+ projectId,
179
+ originalRequest: request,
180
+ requestedBy: actor,
181
+ ...typeof args["title"] === "string" ? { title: args["title"] } : {},
182
+ ...typeof args["requestType"] === "string" ? { requestType: args["requestType"] } : {},
183
+ ...typeof args["priority"] === "string" ? { priority: args["priority"] } : {},
184
+ ...typeof args["idempotencyKey"] === "string" ? { idempotencyKey: args["idempotencyKey"] } : {}
185
+ },
186
+ ctx
187
+ );
188
+ const submitted = await service.submitIntake(result.record.id, ctx);
189
+ return {
190
+ content: {
191
+ intakeId: submitted.record.id,
192
+ title: submitted.record.title,
193
+ requestType: submitted.record.requestType,
194
+ status: submitted.record.status,
195
+ projectId,
196
+ created: result.created,
197
+ idempotent: submitted.idempotent
198
+ },
199
+ isError: false
200
+ };
201
+ }
202
+ async function listIntakes(args) {
203
+ const projectId = await resolveProjectId(false);
204
+ const ctx = intakeContext(projectId, actor);
205
+ const statuses = filterStatuses(args["statuses"]);
206
+ const records = await service.listIntakes(projectId, ctx, statuses ? { statuses } : void 0);
207
+ return {
208
+ content: {
209
+ projectId,
210
+ count: records.length,
211
+ intakes: records.map((record) => ({
212
+ id: record.id,
213
+ title: record.title,
214
+ requestType: record.requestType,
215
+ status: record.status,
216
+ priority: record.priority,
217
+ updatedAt: record.updatedAt,
218
+ createdAt: record.createdAt
219
+ }))
220
+ },
221
+ isError: false
222
+ };
223
+ }
224
+ }
225
+ function filterStatuses(value) {
226
+ if (!Array.isArray(value)) return void 0;
227
+ const known = value.filter(
228
+ (item) => INTAKE_STATUSES.includes(String(item))
229
+ );
230
+ return known.length > 0 ? known : void 0;
231
+ }
232
+ function createRequirementIntakeMcpServer(projectRoot, opts = {}) {
233
+ return new MCPServer({
234
+ host: createRequirementIntakeMcpToolHost(projectRoot, opts),
235
+ serverInfo: { name: "wrongstack-requirement-intake-mcp", version: SERVER_INFO.version },
236
+ prompts: [
237
+ {
238
+ name: "file-requirement-intake",
239
+ title: "File a WrongStack requirement intake",
240
+ description: "Record and submit an unstructured software development request as a structured intake record.",
241
+ arguments: [{ name: "request", description: "The exact request text", required: true }],
242
+ template: "Use requirement_intake_submit to record and submit the request {{request}} verbatim. If a matching intake record may already exist, list records first and reuse the idempotency key pattern."
243
+ }
244
+ ]
245
+ });
246
+ }
247
+ export {
248
+ REQUIREMENT_INTAKE_READ_TOOLS,
249
+ REQUIREMENT_INTAKE_WRITE_TOOLS,
250
+ SERVER_INFO,
251
+ createRequirementIntakeMcpServer,
252
+ createRequirementIntakeMcpToolHost,
253
+ selectRequirementIntakeTools
254
+ };
255
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Requirements Intake MCP — capability tiers.
3
+ *
4
+ * Mirrors the Kanban MCP policy shape: read-only tooling is always exposed;
5
+ * mutation (filing + submitting intake records) requires the writable tier.
6
+ */
7
+ export declare const REQUIREMENT_INTAKE_READ_TOOLS: readonly ['requirement_intake_list'];
8
+ export declare const REQUIREMENT_INTAKE_WRITE_TOOLS: readonly ['requirement_intake_submit'];
9
+ export type RequirementIntakeMcpToolName = (typeof REQUIREMENT_INTAKE_READ_TOOLS)[number] | (typeof REQUIREMENT_INTAKE_WRITE_TOOLS)[number];
10
+ export interface RequirementIntakeMcpPolicyOptions {
11
+ /** Expose the writable `requirement_intake_submit` tool. Default: read-only. */
12
+ writable?: boolean;
13
+ }
14
+ export interface RequirementIntakeMcpToolPolicy {
15
+ name: RequirementIntakeMcpToolName;
16
+ }
17
+ export declare function selectRequirementIntakeTools(opts?: RequirementIntakeMcpPolicyOptions): RequirementIntakeMcpToolPolicy[];
18
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1,5 @@
1
+ export declare const SERVER_INFO: {
2
+ name: string;
3
+ version: string;
4
+ };
5
+ //# sourceMappingURL=version.d.ts.map
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@wrongstack/requirement-intake-mcp",
3
+ "version": "0.299.0",
4
+ "license": "MIT",
5
+ "description": "WrongStack Requirements Intake as a project-scoped MCP server: list intake records (read tier) and file+submit intake records (writable tier).",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/WrongStack/WrongStack.git",
9
+ "directory": "packages/requirement-intake-mcp"
10
+ },
11
+ "homepage": "https://github.com/WrongStack/WrongStack#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/WrongStack/WrongStack/issues"
14
+ },
15
+ "author": "ECOSTACK TECHNOLOGY OÜ",
16
+ "type": "module",
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "bin": {
20
+ "wstack-requirement-intake-mcp": "./dist/cli.js"
21
+ },
22
+ "sideEffects": false,
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "!dist/**/*.map",
32
+ "README.md"
33
+ ],
34
+ "dependencies": {
35
+ "@wrongstack/mcp": "0.299.0",
36
+ "@wrongstack/core": "0.299.0",
37
+ "@wrongstack/requirement-intake": "0.299.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^26.1.2",
41
+ "typescript": "^7.0.2",
42
+ "vitest": "^4.1.10"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "scripts": {
48
+ "build": "node ../../scripts/build-package.mjs",
49
+ "typecheck": "tsc --noEmit -p tsconfig.test.json",
50
+ "test": "echo \"Run @wrongstack/requirement-intake-mcp tests from the workspace root: pnpm exec vitest run packages/requirement-intake-mcp/tests\"",
51
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\""
52
+ }
53
+ }