@wrongstack/kanban-mcp 0.297.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 +21 -0
- package/README.md +36 -0
- package/dist/adapter.d.ts +20 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +443 -0
- package/dist/cli.js.map +7 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +278 -0
- package/dist/index.js.map +7 -0
- package/dist/policy.d.ts +19 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +53 -0
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,36 @@
|
|
|
1
|
+
# @wrongstack/kanban-mcp
|
|
2
|
+
|
|
3
|
+
Expose one WrongStack project's authoritative Kanban service to MCP clients without opening its
|
|
4
|
+
SQLite database or legacy files from the MCP process.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
wstack-kanban-mcp --project-root /absolute/project/path
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The default stdio server exposes `kanban_read` and `kanban_watch`. Add `--writable` for
|
|
11
|
+
`kanban_manage`. Use `--destructive` for the complete external management surface, including
|
|
12
|
+
create/update/move/assignment/verification plus delete, merge, and transfer; it implies
|
|
13
|
+
`--writable`.
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"wrongstack-kanban": {
|
|
19
|
+
"command": "wstack-kanban-mcp",
|
|
20
|
+
"args": ["--project-root", "/absolute/project/path", "--destructive"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
HTTP is available with `--http --port 8766`. A non-loopback bind is refused unless `--token` is
|
|
27
|
+
provided. Use `--actor <stable-agent-id>` so Kanban activity identifies the external caller.
|
|
28
|
+
|
|
29
|
+
`kanban_watch` is a bounded long poll over the daemon's mutation stream. Events are wake-up hints,
|
|
30
|
+
not state snapshots: call `kanban_read` after an event, timeout, or disconnect to reconcile the
|
|
31
|
+
authoritative board revision.
|
|
32
|
+
|
|
33
|
+
The bundled external-agent workflow is
|
|
34
|
+
`@wrongstack/core/skills/wrongstack-kanban/SKILL.md`. Copy the complete `wrongstack-kanban` folder
|
|
35
|
+
from the installed `@wrongstack/core/skills` directory into the coding tool's project or user skill
|
|
36
|
+
directory (for example `.claude/skills/` or `.codex/skills/`).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Context } from '@wrongstack/core/agent';
|
|
2
|
+
import { type KanbanServerEvent } from '@wrongstack/kanban';
|
|
3
|
+
import { MCPServer, type MCPServerToolHost } from '@wrongstack/mcp';
|
|
4
|
+
import { type KanbanMcpPolicyOptions } from './policy.js';
|
|
5
|
+
interface KanbanEventConnection {
|
|
6
|
+
subscribe(listener: (event: KanbanServerEvent) => void): () => void;
|
|
7
|
+
onDisconnect(listener: () => void): () => void;
|
|
8
|
+
}
|
|
9
|
+
export interface KanbanMcpDependencies {
|
|
10
|
+
executeKanban?: (args: Record<string, unknown>, context: Context, signal: AbortSignal) => Promise<unknown>;
|
|
11
|
+
getConnection?: (projectRoot: string) => Promise<KanbanEventConnection | null>;
|
|
12
|
+
}
|
|
13
|
+
export interface KanbanMcpToolHostOptions extends KanbanMcpPolicyOptions {
|
|
14
|
+
actor?: string | undefined;
|
|
15
|
+
dependencies?: KanbanMcpDependencies | undefined;
|
|
16
|
+
}
|
|
17
|
+
export declare function createKanbanMcpToolHost(projectRoot: string, opts?: KanbanMcpToolHostOptions): MCPServerToolHost;
|
|
18
|
+
export declare function createKanbanMcpServer(projectRoot: string, opts?: KanbanMcpToolHostOptions): MCPServer;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAA6B,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvF,OAAO,EACL,SAAS,EAGT,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAEL,KAAK,sBAAsB,EAG5B,MAAM,aAAa,CAAC;AAMrB,UAAU,qBAAqB;IAC7B,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACpE,YAAY,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,CAAC,EAAE,CACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,WAAW,KAChB,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;CAChF;AAED,MAAM,WAAW,wBAAyB,SAAQ,sBAAsB;IACtE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,YAAY,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAC;CAClD;AAyHD,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,wBAA6B,GAClC,iBAAiB,CA6DnB;AAED,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,wBAA6B,GAClC,SAAS,CAgBX"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
destructive: boolean;
|
|
10
|
+
actor?: string | undefined;
|
|
11
|
+
help: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function printHelp(stdout: NodeJS.WriteStream): void;
|
|
14
|
+
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
15
|
+
export declare function main(argv?: string[]): Promise<number>;
|
|
16
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAUA,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,GAAG,IAAI,CAqB1D;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAmD7D;AAED,wBAAsB,IAAI,CAAC,IAAI,WAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6DxE"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
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 { closeKanbanServerConnections, getKanbanServerConnection as getKanbanServerConnection2 } from "@wrongstack/kanban";
|
|
9
|
+
import { serveHttp, serveStdio } from "@wrongstack/mcp";
|
|
10
|
+
|
|
11
|
+
// src/adapter.ts
|
|
12
|
+
import { getKanbanServerConnection } from "@wrongstack/kanban";
|
|
13
|
+
import {
|
|
14
|
+
MCPServer
|
|
15
|
+
} from "@wrongstack/mcp";
|
|
16
|
+
import { kanbanTool } from "@wrongstack/tools/kanban";
|
|
17
|
+
|
|
18
|
+
// src/policy.ts
|
|
19
|
+
var KANBAN_READ_ACTIONS = [
|
|
20
|
+
"list_boards",
|
|
21
|
+
"get_board",
|
|
22
|
+
"export_markdown",
|
|
23
|
+
"export_task_graph",
|
|
24
|
+
"search_tasks",
|
|
25
|
+
"ready_tasks",
|
|
26
|
+
"snapshot",
|
|
27
|
+
"get_task",
|
|
28
|
+
"get_chain",
|
|
29
|
+
"events",
|
|
30
|
+
"queue_health"
|
|
31
|
+
];
|
|
32
|
+
var KANBAN_MANAGE_ACTIONS = [
|
|
33
|
+
"create_board",
|
|
34
|
+
"duplicate_board",
|
|
35
|
+
"update_board",
|
|
36
|
+
"adopt_managed_lifecycle",
|
|
37
|
+
"generate_board",
|
|
38
|
+
"sync_task_graph",
|
|
39
|
+
"create_from_graph",
|
|
40
|
+
"import_session_tasks",
|
|
41
|
+
"add_column",
|
|
42
|
+
"update_column",
|
|
43
|
+
"add_task",
|
|
44
|
+
"split_task",
|
|
45
|
+
"copy_task",
|
|
46
|
+
"update_task",
|
|
47
|
+
"transition_task",
|
|
48
|
+
"repair_managed_projection",
|
|
49
|
+
"move_task",
|
|
50
|
+
"set_chain",
|
|
51
|
+
"claim_task",
|
|
52
|
+
"release_task",
|
|
53
|
+
"assign_task",
|
|
54
|
+
"mark_assignment",
|
|
55
|
+
"heartbeat_assignment",
|
|
56
|
+
"recover_stale",
|
|
57
|
+
"add_dependency",
|
|
58
|
+
"add_goal_metric",
|
|
59
|
+
"update_goal_metric",
|
|
60
|
+
"add_check",
|
|
61
|
+
"update_check",
|
|
62
|
+
"add_note",
|
|
63
|
+
"add_link",
|
|
64
|
+
"verify_completion",
|
|
65
|
+
"split_atomic",
|
|
66
|
+
"assess_atomicity",
|
|
67
|
+
"propose_decomposition"
|
|
68
|
+
];
|
|
69
|
+
var KANBAN_DESTRUCTIVE_ACTIONS = [
|
|
70
|
+
"delete_board",
|
|
71
|
+
"delete_column",
|
|
72
|
+
"delete_task",
|
|
73
|
+
"merge_tasks",
|
|
74
|
+
"transfer_task"
|
|
75
|
+
];
|
|
76
|
+
function selectKanbanMcpTools(opts = {}) {
|
|
77
|
+
const tools = [
|
|
78
|
+
{ name: "kanban_read", actions: KANBAN_READ_ACTIONS },
|
|
79
|
+
{ name: "kanban_watch" }
|
|
80
|
+
];
|
|
81
|
+
if (opts.writable === true || opts.destructive === true) {
|
|
82
|
+
tools.push({ name: "kanban_manage", actions: KANBAN_MANAGE_ACTIONS });
|
|
83
|
+
}
|
|
84
|
+
if (opts.destructive === true) {
|
|
85
|
+
tools.push({ name: "kanban_destructive", actions: KANBAN_DESTRUCTIVE_ACTIONS });
|
|
86
|
+
}
|
|
87
|
+
return tools;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/version.ts
|
|
91
|
+
import { readFileSync } from "node:fs";
|
|
92
|
+
import { dirname, resolve } from "node:path";
|
|
93
|
+
import { fileURLToPath } from "node:url";
|
|
94
|
+
var here = dirname(fileURLToPath(import.meta.url));
|
|
95
|
+
var packagePath = resolve(here, "..", "package.json");
|
|
96
|
+
var cached;
|
|
97
|
+
function readServerInfo() {
|
|
98
|
+
if (cached) return cached;
|
|
99
|
+
try {
|
|
100
|
+
const pkg = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
101
|
+
cached = {
|
|
102
|
+
name: pkg.name ?? "@wrongstack/kanban-mcp",
|
|
103
|
+
version: pkg.version ?? "0.0.0"
|
|
104
|
+
};
|
|
105
|
+
} catch {
|
|
106
|
+
cached = { name: "@wrongstack/kanban-mcp", version: "0.0.0" };
|
|
107
|
+
}
|
|
108
|
+
return cached;
|
|
109
|
+
}
|
|
110
|
+
var SERVER_INFO = readServerInfo();
|
|
111
|
+
|
|
112
|
+
// src/adapter.ts
|
|
113
|
+
var WATCH_DEFAULT_TIMEOUT_MS = 15e3;
|
|
114
|
+
var WATCH_MAX_TIMEOUT_MS = 25e3;
|
|
115
|
+
function actionSchema(actions) {
|
|
116
|
+
const schema = structuredClone(kanbanTool.inputSchema);
|
|
117
|
+
const properties = schema["properties"];
|
|
118
|
+
if (!properties || typeof properties !== "object" || Array.isArray(properties)) {
|
|
119
|
+
throw new Error("Kanban MCP: Kanban tool schema has no properties object");
|
|
120
|
+
}
|
|
121
|
+
const action = properties["action"];
|
|
122
|
+
if (!action || typeof action !== "object" || Array.isArray(action)) {
|
|
123
|
+
throw new Error("Kanban MCP: Kanban tool schema has no action property");
|
|
124
|
+
}
|
|
125
|
+
action["enum"] = [...actions];
|
|
126
|
+
return schema;
|
|
127
|
+
}
|
|
128
|
+
var WATCH_SCHEMA = {
|
|
129
|
+
type: "object",
|
|
130
|
+
properties: {
|
|
131
|
+
boardId: {
|
|
132
|
+
type: "string",
|
|
133
|
+
description: "Optional full board id. Omit to observe any board."
|
|
134
|
+
},
|
|
135
|
+
timeoutMs: {
|
|
136
|
+
type: "number",
|
|
137
|
+
minimum: 100,
|
|
138
|
+
maximum: WATCH_MAX_TIMEOUT_MS,
|
|
139
|
+
description: `Long-poll timeout. Defaults to ${WATCH_DEFAULT_TIMEOUT_MS}.`
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
additionalProperties: false
|
|
143
|
+
};
|
|
144
|
+
var TOOL_DESCRIPTIONS = {
|
|
145
|
+
kanban_read: "Read authoritative WrongStack Kanban boards, tasks, events, queue health, task chains, and exports through the project IPC owner.",
|
|
146
|
+
kanban_manage: "Create and update WrongStack Kanban boards and tasks, assignments, dependencies, evidence, verification, and orchestration state. Requires server --writable.",
|
|
147
|
+
kanban_destructive: "Delete or absorb durable WrongStack Kanban state, including board/task/column deletion, merge, and cross-board transfer. Requires server --destructive.",
|
|
148
|
+
kanban_watch: "Wait for the next project Kanban mutation event. After any event or timeout, reconcile authoritative state with kanban_read."
|
|
149
|
+
};
|
|
150
|
+
function toolDescriptor(name, actions) {
|
|
151
|
+
return {
|
|
152
|
+
name,
|
|
153
|
+
description: TOOL_DESCRIPTIONS[name],
|
|
154
|
+
inputSchema: actions ? actionSchema(actions) : WATCH_SCHEMA
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function createContext(projectRoot, actor) {
|
|
158
|
+
return {
|
|
159
|
+
systemPrompt: [],
|
|
160
|
+
cwd: projectRoot,
|
|
161
|
+
projectRoot,
|
|
162
|
+
allowOutsideProjectRoot: false,
|
|
163
|
+
model: "kanban-mcp",
|
|
164
|
+
tools: [],
|
|
165
|
+
meta: { source: "kanban-mcp" },
|
|
166
|
+
agentId: actor,
|
|
167
|
+
agentName: actor
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function normalizeTimeout(value) {
|
|
171
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return WATCH_DEFAULT_TIMEOUT_MS;
|
|
172
|
+
return Math.min(WATCH_MAX_TIMEOUT_MS, Math.max(100, Math.trunc(value)));
|
|
173
|
+
}
|
|
174
|
+
function eventBoardId(event) {
|
|
175
|
+
if (!event.data || typeof event.data !== "object" || Array.isArray(event.data)) return void 0;
|
|
176
|
+
const boardId = event.data["boardId"];
|
|
177
|
+
return typeof boardId === "string" ? boardId : void 0;
|
|
178
|
+
}
|
|
179
|
+
async function watchForKanbanMutation(projectRoot, args, getConnection) {
|
|
180
|
+
const boardId = typeof args["boardId"] === "string" ? args["boardId"] : void 0;
|
|
181
|
+
const timeoutMs = normalizeTimeout(args["timeoutMs"]);
|
|
182
|
+
const connection = await getConnection(projectRoot);
|
|
183
|
+
if (!connection) {
|
|
184
|
+
throw new Error("Kanban project server is disabled; live watch is unavailable");
|
|
185
|
+
}
|
|
186
|
+
return await new Promise((resolve3) => {
|
|
187
|
+
let settled = false;
|
|
188
|
+
let unsubscribeEvent = () => {
|
|
189
|
+
};
|
|
190
|
+
let unsubscribeDisconnect = () => {
|
|
191
|
+
};
|
|
192
|
+
let timer;
|
|
193
|
+
const finish = (result) => {
|
|
194
|
+
if (settled) return;
|
|
195
|
+
settled = true;
|
|
196
|
+
if (timer) clearTimeout(timer);
|
|
197
|
+
unsubscribeEvent();
|
|
198
|
+
unsubscribeDisconnect();
|
|
199
|
+
resolve3(result);
|
|
200
|
+
};
|
|
201
|
+
unsubscribeEvent = connection.subscribe((event) => {
|
|
202
|
+
const changedBoardId = eventBoardId(event);
|
|
203
|
+
if (boardId && changedBoardId !== boardId) return;
|
|
204
|
+
finish({ changed: true, event: event.event, data: event.data });
|
|
205
|
+
});
|
|
206
|
+
unsubscribeDisconnect = connection.onDisconnect(() => {
|
|
207
|
+
finish({ changed: false, disconnected: true, reason: "Kanban daemon disconnected" });
|
|
208
|
+
});
|
|
209
|
+
timer = setTimeout(() => {
|
|
210
|
+
finish({ changed: false, timedOut: true, timeoutMs });
|
|
211
|
+
}, timeoutMs);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
function createKanbanMcpToolHost(projectRoot, opts = {}) {
|
|
215
|
+
const policy = selectKanbanMcpTools(opts);
|
|
216
|
+
const allowed = new Map(policy.map((entry) => [entry.name, entry.actions]));
|
|
217
|
+
const actor = opts.actor?.trim() || "external-kanban-mcp";
|
|
218
|
+
const context = createContext(projectRoot, actor);
|
|
219
|
+
const executeKanban = opts.dependencies?.executeKanban ?? (async (args, ctx, signal) => await kanbanTool.execute(args, ctx, { signal }));
|
|
220
|
+
const getConnection = opts.dependencies?.getConnection ?? getKanbanServerConnection;
|
|
221
|
+
return {
|
|
222
|
+
listTools() {
|
|
223
|
+
return policy.map((entry) => toolDescriptor(entry.name, entry.actions));
|
|
224
|
+
},
|
|
225
|
+
async callTool(name, args) {
|
|
226
|
+
if (!allowed.has(name)) {
|
|
227
|
+
return {
|
|
228
|
+
content: `Tool "${name}" is not exposed by this Kanban MCP server`,
|
|
229
|
+
isError: true
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
if (name === "kanban_watch") {
|
|
233
|
+
try {
|
|
234
|
+
const content = await watchForKanbanMutation(projectRoot, args, getConnection);
|
|
235
|
+
return { content, isError: false };
|
|
236
|
+
} catch (error) {
|
|
237
|
+
return {
|
|
238
|
+
content: error instanceof Error ? error.message : String(error),
|
|
239
|
+
isError: true
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const actions = allowed.get(name);
|
|
244
|
+
const action = args["action"];
|
|
245
|
+
if (typeof action !== "string" || !actions?.includes(action)) {
|
|
246
|
+
return {
|
|
247
|
+
content: `Action "${String(action)}" is not allowed through ${name}`,
|
|
248
|
+
isError: true
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
const result = await executeKanban(args, context, new AbortController().signal);
|
|
253
|
+
const failed = !!result && typeof result === "object" && !Array.isArray(result) && result["ok"] === false;
|
|
254
|
+
return { content: result, isError: failed };
|
|
255
|
+
} catch (error) {
|
|
256
|
+
return {
|
|
257
|
+
content: error instanceof Error ? error.message : String(error),
|
|
258
|
+
isError: true
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function createKanbanMcpServer(projectRoot, opts = {}) {
|
|
265
|
+
return new MCPServer({
|
|
266
|
+
host: createKanbanMcpToolHost(projectRoot, opts),
|
|
267
|
+
serverInfo: { name: "wrongstack-kanban-mcp", version: SERVER_INFO.version },
|
|
268
|
+
prompts: [
|
|
269
|
+
{
|
|
270
|
+
name: "work-kanban-task",
|
|
271
|
+
title: "Work a WrongStack Kanban task",
|
|
272
|
+
description: "Inspect, claim, execute, verify, and update a task without bypassing board policy.",
|
|
273
|
+
arguments: [{ name: "boardId", description: "Board id or unique prefix", required: true }],
|
|
274
|
+
template: "Use the WrongStack Kanban MCP tools on board {{boardId}}. Re-read authoritative board state before mutation, claim an eligible task, keep assignment state current, attach truthful evidence, and satisfy its completion gate."
|
|
275
|
+
}
|
|
276
|
+
]
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/cli.ts
|
|
281
|
+
function printHelp(stdout) {
|
|
282
|
+
stdout.write(
|
|
283
|
+
[
|
|
284
|
+
`${SERVER_INFO.name} v${SERVER_INFO.version} \u2014 WrongStack Kanban MCP server`,
|
|
285
|
+
"",
|
|
286
|
+
"Usage:",
|
|
287
|
+
` ${SERVER_INFO.name} --project-root <path> [options]`,
|
|
288
|
+
"",
|
|
289
|
+
"Options:",
|
|
290
|
+
" --project-root <path> Project whose IPC-backed Kanban should be served (required).",
|
|
291
|
+
" --stdio Use stdio transport (default).",
|
|
292
|
+
" --http Use HTTP transport.",
|
|
293
|
+
" --port <n> HTTP port (default 0 = ephemeral).",
|
|
294
|
+
" --host <h> HTTP bind host (default 127.0.0.1).",
|
|
295
|
+
" --token <t> Bearer token. Required for a non-loopback HTTP bind.",
|
|
296
|
+
" --actor <id> Actor id recorded in Kanban activity/events.",
|
|
297
|
+
" --writable Expose non-destructive board/task management.",
|
|
298
|
+
" --destructive Also expose delete/merge/transfer; implies --writable.",
|
|
299
|
+
" -h, --help Show this message."
|
|
300
|
+
].join("\n") + "\n"
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
function parseArgs(argv) {
|
|
304
|
+
const parsed = {
|
|
305
|
+
projectRoot: "",
|
|
306
|
+
transport: "stdio",
|
|
307
|
+
httpPort: 0,
|
|
308
|
+
httpHost: "127.0.0.1",
|
|
309
|
+
writable: false,
|
|
310
|
+
destructive: false,
|
|
311
|
+
help: false
|
|
312
|
+
};
|
|
313
|
+
for (let index = 0; index < argv.length; index++) {
|
|
314
|
+
const arg = argv[index];
|
|
315
|
+
switch (arg) {
|
|
316
|
+
case "--project-root":
|
|
317
|
+
parsed.projectRoot = path.resolve(argv[++index] ?? "");
|
|
318
|
+
break;
|
|
319
|
+
case "--stdio":
|
|
320
|
+
parsed.transport = "stdio";
|
|
321
|
+
break;
|
|
322
|
+
case "--http":
|
|
323
|
+
parsed.transport = "http";
|
|
324
|
+
break;
|
|
325
|
+
case "--port":
|
|
326
|
+
parsed.httpPort = Number(argv[++index] ?? "") || 0;
|
|
327
|
+
break;
|
|
328
|
+
case "--host":
|
|
329
|
+
parsed.httpHost = argv[++index] ?? "127.0.0.1";
|
|
330
|
+
break;
|
|
331
|
+
case "--token":
|
|
332
|
+
parsed.httpToken = argv[++index];
|
|
333
|
+
break;
|
|
334
|
+
case "--actor":
|
|
335
|
+
parsed.actor = argv[++index];
|
|
336
|
+
break;
|
|
337
|
+
case "--writable":
|
|
338
|
+
parsed.writable = true;
|
|
339
|
+
break;
|
|
340
|
+
case "--destructive":
|
|
341
|
+
parsed.destructive = true;
|
|
342
|
+
parsed.writable = true;
|
|
343
|
+
break;
|
|
344
|
+
case "-h":
|
|
345
|
+
case "--help":
|
|
346
|
+
parsed.help = true;
|
|
347
|
+
break;
|
|
348
|
+
default:
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return parsed;
|
|
353
|
+
}
|
|
354
|
+
async function main(argv = process.argv.slice(2)) {
|
|
355
|
+
const args = parseArgs(argv);
|
|
356
|
+
if (args.help) {
|
|
357
|
+
printHelp(process.stdout);
|
|
358
|
+
return 0;
|
|
359
|
+
}
|
|
360
|
+
if (!args.projectRoot) {
|
|
361
|
+
process.stderr.write(`${SERVER_INFO.name}: --project-root is required
|
|
362
|
+
`);
|
|
363
|
+
printHelp(process.stderr);
|
|
364
|
+
return 2;
|
|
365
|
+
}
|
|
366
|
+
const projectRoot = canonicalProjectRoot(args.projectRoot);
|
|
367
|
+
try {
|
|
368
|
+
const connection = await getKanbanServerConnection2(projectRoot);
|
|
369
|
+
if (!connection) throw new Error("Kanban project server is disabled");
|
|
370
|
+
await connection.request("ping", {});
|
|
371
|
+
} catch (error) {
|
|
372
|
+
process.stderr.write(
|
|
373
|
+
`${SERVER_INFO.name}: cannot attach to Kanban project server for ${projectRoot}: ${error instanceof Error ? error.message : String(error)}
|
|
374
|
+
`
|
|
375
|
+
);
|
|
376
|
+
return 3;
|
|
377
|
+
}
|
|
378
|
+
const server = createKanbanMcpServer(projectRoot, {
|
|
379
|
+
writable: args.writable,
|
|
380
|
+
destructive: args.destructive,
|
|
381
|
+
...args.actor ? { actor: args.actor } : {}
|
|
382
|
+
});
|
|
383
|
+
const policyText = `writable=${String(args.writable)} destructive=${String(args.destructive)}`;
|
|
384
|
+
if (args.transport === "http") {
|
|
385
|
+
const handle2 = await serveHttp(server, {
|
|
386
|
+
port: args.httpPort,
|
|
387
|
+
host: args.httpHost,
|
|
388
|
+
...args.httpToken ? { token: args.httpToken } : {},
|
|
389
|
+
logger: { warn: (message) => process.stderr.write(`[kanban-mcp] ${message}
|
|
390
|
+
`) }
|
|
391
|
+
});
|
|
392
|
+
process.stderr.write(
|
|
393
|
+
`${SERVER_INFO.name}: ready at ${handle2.url} \u2014 projectRoot=${projectRoot} transport=http ${policyText}${args.httpToken ? " [token auth]" : ""}
|
|
394
|
+
`
|
|
395
|
+
);
|
|
396
|
+
await new Promise((resolve3) => {
|
|
397
|
+
process.once("SIGINT", resolve3);
|
|
398
|
+
process.once("SIGTERM", resolve3);
|
|
399
|
+
});
|
|
400
|
+
await handle2.close();
|
|
401
|
+
closeKanbanServerConnections();
|
|
402
|
+
return 0;
|
|
403
|
+
}
|
|
404
|
+
const handle = serveStdio(server);
|
|
405
|
+
process.stderr.write(
|
|
406
|
+
`${SERVER_INFO.name}: ready on stdio \u2014 projectRoot=${projectRoot} transport=stdio ${policyText}
|
|
407
|
+
`
|
|
408
|
+
);
|
|
409
|
+
await handle.done;
|
|
410
|
+
closeKanbanServerConnections();
|
|
411
|
+
return 0;
|
|
412
|
+
}
|
|
413
|
+
function isMainModule() {
|
|
414
|
+
const entry = process.argv[1];
|
|
415
|
+
if (!entry) return false;
|
|
416
|
+
const self = fileURLToPath2(import.meta.url);
|
|
417
|
+
if (path.resolve(entry) === self) return true;
|
|
418
|
+
try {
|
|
419
|
+
return realpathSync(entry) === realpathSync(self);
|
|
420
|
+
} catch {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (isMainModule()) {
|
|
425
|
+
main().then(
|
|
426
|
+
(code) => {
|
|
427
|
+
process.exitCode = code;
|
|
428
|
+
},
|
|
429
|
+
(error) => {
|
|
430
|
+
process.stderr.write(`${SERVER_INFO.name}: unexpected error
|
|
431
|
+
`);
|
|
432
|
+
process.stderr.write(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
433
|
+
process.stderr.write("\n");
|
|
434
|
+
process.exitCode = 1;
|
|
435
|
+
}
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
export {
|
|
439
|
+
main,
|
|
440
|
+
parseArgs,
|
|
441
|
+
printHelp
|
|
442
|
+
};
|
|
443
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli.ts", "../src/adapter.ts", "../src/policy.ts", "../src/version.ts"],
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\nimport { realpathSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { canonicalProjectRoot } from '@wrongstack/core/utils';\nimport { closeKanbanServerConnections, getKanbanServerConnection } from '@wrongstack/kanban';\nimport { serveHttp, serveStdio } from '@wrongstack/mcp';\nimport { createKanbanMcpServer } from './adapter.js';\nimport { SERVER_INFO } from './version.js';\n\nexport interface ParsedArgs {\n projectRoot: string;\n transport: 'stdio' | 'http';\n httpPort: number;\n httpHost: string;\n httpToken?: string | undefined;\n writable: boolean;\n destructive: boolean;\n actor?: string | undefined;\n help: boolean;\n}\n\nexport function printHelp(stdout: NodeJS.WriteStream): void {\n stdout.write(\n [\n `${SERVER_INFO.name} v${SERVER_INFO.version} \u2014 WrongStack Kanban MCP server`,\n '',\n 'Usage:',\n ` ${SERVER_INFO.name} --project-root <path> [options]`,\n '',\n 'Options:',\n ' --project-root <path> Project whose IPC-backed Kanban should be served (required).',\n ' --stdio Use stdio transport (default).',\n ' --http Use HTTP transport.',\n ' --port <n> HTTP port (default 0 = ephemeral).',\n ' --host <h> HTTP bind host (default 127.0.0.1).',\n ' --token <t> Bearer token. Required for a non-loopback HTTP bind.',\n ' --actor <id> Actor id recorded in Kanban activity/events.',\n ' --writable Expose non-destructive board/task management.',\n ' --destructive Also expose delete/merge/transfer; implies --writable.',\n ' -h, --help Show this message.',\n ].join('\\n') + '\\n',\n );\n}\n\nexport function parseArgs(argv: readonly string[]): ParsedArgs {\n const parsed: ParsedArgs = {\n projectRoot: '',\n transport: 'stdio',\n httpPort: 0,\n httpHost: '127.0.0.1',\n writable: false,\n destructive: false,\n help: false,\n };\n\n for (let index = 0; index < argv.length; index++) {\n const arg = argv[index];\n switch (arg) {\n case '--project-root':\n parsed.projectRoot = path.resolve(argv[++index] ?? '');\n break;\n case '--stdio':\n parsed.transport = 'stdio';\n break;\n case '--http':\n parsed.transport = 'http';\n break;\n case '--port':\n parsed.httpPort = Number(argv[++index] ?? '') || 0;\n break;\n case '--host':\n parsed.httpHost = argv[++index] ?? '127.0.0.1';\n break;\n case '--token':\n parsed.httpToken = argv[++index];\n break;\n case '--actor':\n parsed.actor = argv[++index];\n break;\n case '--writable':\n parsed.writable = true;\n break;\n case '--destructive':\n parsed.destructive = true;\n parsed.writable = true;\n break;\n case '-h':\n case '--help':\n parsed.help = true;\n break;\n default:\n break;\n }\n }\n return parsed;\n}\n\nexport async function main(argv = process.argv.slice(2)): Promise<number> {\n const args = parseArgs(argv);\n if (args.help) {\n printHelp(process.stdout);\n return 0;\n }\n if (!args.projectRoot) {\n process.stderr.write(`${SERVER_INFO.name}: --project-root is required\\n`);\n printHelp(process.stderr);\n return 2;\n }\n\n const projectRoot = canonicalProjectRoot(args.projectRoot);\n try {\n const connection = await getKanbanServerConnection(projectRoot);\n if (!connection) throw new Error('Kanban project server is disabled');\n await connection.request('ping', {});\n } catch (error) {\n process.stderr.write(\n `${SERVER_INFO.name}: cannot attach to Kanban project server for ${projectRoot}: ${\n error instanceof Error ? error.message : String(error)\n }\\n`,\n );\n return 3;\n }\n\n const server = createKanbanMcpServer(projectRoot, {\n writable: args.writable,\n destructive: args.destructive,\n ...(args.actor ? { actor: args.actor } : {}),\n });\n const policyText = `writable=${String(args.writable)} destructive=${String(args.destructive)}`;\n\n if (args.transport === 'http') {\n const handle = await serveHttp(server, {\n port: args.httpPort,\n host: args.httpHost,\n ...(args.httpToken ? { token: args.httpToken } : {}),\n logger: { warn: (message) => process.stderr.write(`[kanban-mcp] ${message}\\n`) },\n });\n process.stderr.write(\n `${SERVER_INFO.name}: ready at ${handle.url} \u2014 projectRoot=${projectRoot} transport=http ${policyText}${\n args.httpToken ? ' [token auth]' : ''\n }\\n`,\n );\n await new Promise<void>((resolve) => {\n process.once('SIGINT', resolve);\n process.once('SIGTERM', resolve);\n });\n await handle.close();\n closeKanbanServerConnections();\n return 0;\n }\n\n const handle = serveStdio(server);\n process.stderr.write(\n `${SERVER_INFO.name}: ready on stdio \u2014 projectRoot=${projectRoot} transport=stdio ${policyText}\\n`,\n );\n await handle.done;\n closeKanbanServerConnections();\n return 0;\n}\n\nfunction isMainModule(): boolean {\n const entry = process.argv[1];\n if (!entry) return false;\n const self = fileURLToPath(import.meta.url);\n if (path.resolve(entry) === self) return true;\n try {\n return realpathSync(entry) === realpathSync(self);\n } catch {\n return false;\n }\n}\n\nif (isMainModule()) {\n main().then(\n (code) => {\n process.exitCode = code;\n },\n (error) => {\n process.stderr.write(`${SERVER_INFO.name}: unexpected error\\n`);\n process.stderr.write(error instanceof Error ? (error.stack ?? error.message) : String(error));\n process.stderr.write('\\n');\n process.exitCode = 1;\n },\n );\n}\n", "import type { Context } from '@wrongstack/core/agent';\nimport { getKanbanServerConnection, type KanbanServerEvent } from '@wrongstack/kanban';\nimport {\n MCPServer,\n type MCPServerCallResult,\n type MCPServerTool,\n type MCPServerToolHost,\n} from '@wrongstack/mcp';\nimport { kanbanTool } from '@wrongstack/tools/kanban';\nimport {\n type KanbanMcpAction,\n type KanbanMcpPolicyOptions,\n type KanbanMcpToolName,\n selectKanbanMcpTools,\n} from './policy.js';\nimport { SERVER_INFO } from './version.js';\n\nconst WATCH_DEFAULT_TIMEOUT_MS = 15_000;\nconst WATCH_MAX_TIMEOUT_MS = 25_000;\n\ninterface KanbanEventConnection {\n subscribe(listener: (event: KanbanServerEvent) => void): () => void;\n onDisconnect(listener: () => void): () => void;\n}\n\nexport interface KanbanMcpDependencies {\n executeKanban?: (\n args: Record<string, unknown>,\n context: Context,\n signal: AbortSignal,\n ) => Promise<unknown>;\n getConnection?: (projectRoot: string) => Promise<KanbanEventConnection | null>;\n}\n\nexport interface KanbanMcpToolHostOptions extends KanbanMcpPolicyOptions {\n actor?: string | undefined;\n dependencies?: KanbanMcpDependencies | undefined;\n}\n\nfunction actionSchema(actions: readonly KanbanMcpAction[]): Record<string, unknown> {\n const schema = structuredClone(kanbanTool.inputSchema) as Record<string, unknown>;\n const properties = schema['properties'];\n if (!properties || typeof properties !== 'object' || Array.isArray(properties)) {\n throw new Error('Kanban MCP: Kanban tool schema has no properties object');\n }\n const action = (properties as Record<string, unknown>)['action'];\n if (!action || typeof action !== 'object' || Array.isArray(action)) {\n throw new Error('Kanban MCP: Kanban tool schema has no action property');\n }\n (action as Record<string, unknown>)['enum'] = [...actions];\n return schema;\n}\n\nconst WATCH_SCHEMA: Record<string, unknown> = {\n type: 'object',\n properties: {\n boardId: {\n type: 'string',\n description: 'Optional full board id. Omit to observe any board.',\n },\n timeoutMs: {\n type: 'number',\n minimum: 100,\n maximum: WATCH_MAX_TIMEOUT_MS,\n description: `Long-poll timeout. Defaults to ${WATCH_DEFAULT_TIMEOUT_MS}.`,\n },\n },\n additionalProperties: false,\n};\n\nconst TOOL_DESCRIPTIONS: Record<KanbanMcpToolName, string> = {\n kanban_read:\n 'Read authoritative WrongStack Kanban boards, tasks, events, queue health, task chains, and exports through the project IPC owner.',\n kanban_manage:\n 'Create and update WrongStack Kanban boards and tasks, assignments, dependencies, evidence, verification, and orchestration state. Requires server --writable.',\n kanban_destructive:\n 'Delete or absorb durable WrongStack Kanban state, including board/task/column deletion, merge, and cross-board transfer. Requires server --destructive.',\n kanban_watch:\n 'Wait for the next project Kanban mutation event. After any event or timeout, reconcile authoritative state with kanban_read.',\n};\n\nfunction toolDescriptor(\n name: KanbanMcpToolName,\n actions?: readonly KanbanMcpAction[],\n): MCPServerTool {\n return {\n name,\n description: TOOL_DESCRIPTIONS[name],\n inputSchema: actions ? actionSchema(actions) : WATCH_SCHEMA,\n };\n}\n\nfunction createContext(projectRoot: string, actor: string): Context {\n return {\n systemPrompt: [],\n cwd: projectRoot,\n projectRoot,\n allowOutsideProjectRoot: false,\n model: 'kanban-mcp',\n tools: [],\n meta: { source: 'kanban-mcp' },\n agentId: actor,\n agentName: actor,\n } as unknown as Context;\n}\n\nfunction normalizeTimeout(value: unknown): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return WATCH_DEFAULT_TIMEOUT_MS;\n return Math.min(WATCH_MAX_TIMEOUT_MS, Math.max(100, Math.trunc(value)));\n}\n\nfunction eventBoardId(event: KanbanServerEvent): string | undefined {\n if (!event.data || typeof event.data !== 'object' || Array.isArray(event.data)) return undefined;\n const boardId = (event.data as Record<string, unknown>)['boardId'];\n return typeof boardId === 'string' ? boardId : undefined;\n}\n\nasync function watchForKanbanMutation(\n projectRoot: string,\n args: Record<string, unknown>,\n getConnection: (projectRoot: string) => Promise<KanbanEventConnection | null>,\n): Promise<unknown> {\n const boardId = typeof args['boardId'] === 'string' ? args['boardId'] : undefined;\n const timeoutMs = normalizeTimeout(args['timeoutMs']);\n const connection = await getConnection(projectRoot);\n if (!connection) {\n throw new Error('Kanban project server is disabled; live watch is unavailable');\n }\n\n return await new Promise((resolve) => {\n let settled = false;\n let unsubscribeEvent = (): void => {};\n let unsubscribeDisconnect = (): void => {};\n let timer: NodeJS.Timeout | undefined;\n\n const finish = (result: unknown): void => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n unsubscribeEvent();\n unsubscribeDisconnect();\n resolve(result);\n };\n\n unsubscribeEvent = connection.subscribe((event) => {\n const changedBoardId = eventBoardId(event);\n if (boardId && changedBoardId !== boardId) return;\n finish({ changed: true, event: event.event, data: event.data });\n });\n unsubscribeDisconnect = connection.onDisconnect(() => {\n finish({ changed: false, disconnected: true, reason: 'Kanban daemon disconnected' });\n });\n timer = setTimeout(() => {\n finish({ changed: false, timedOut: true, timeoutMs });\n }, timeoutMs);\n });\n}\n\nexport function createKanbanMcpToolHost(\n projectRoot: string,\n opts: KanbanMcpToolHostOptions = {},\n): MCPServerToolHost {\n const policy = selectKanbanMcpTools(opts);\n const allowed = new Map(policy.map((entry) => [entry.name, entry.actions]));\n const actor = opts.actor?.trim() || 'external-kanban-mcp';\n const context = createContext(projectRoot, actor);\n const executeKanban =\n opts.dependencies?.executeKanban ??\n (async (args: Record<string, unknown>, ctx: Context, signal: AbortSignal) =>\n await kanbanTool.execute(args as never, ctx, { signal }));\n const getConnection = opts.dependencies?.getConnection ?? getKanbanServerConnection;\n\n return {\n listTools(): MCPServerTool[] {\n return policy.map((entry) => toolDescriptor(entry.name, entry.actions));\n },\n\n async callTool(name: string, args: Record<string, unknown>): Promise<MCPServerCallResult> {\n if (!allowed.has(name as KanbanMcpToolName)) {\n return {\n content: `Tool \"${name}\" is not exposed by this Kanban MCP server`,\n isError: true,\n };\n }\n\n if (name === 'kanban_watch') {\n try {\n const content = await watchForKanbanMutation(projectRoot, args, getConnection);\n return { content, isError: false };\n } catch (error) {\n return {\n content: error instanceof Error ? error.message : String(error),\n isError: true,\n };\n }\n }\n\n const actions = allowed.get(name as KanbanMcpToolName);\n const action = args['action'];\n if (typeof action !== 'string' || !actions?.includes(action as KanbanMcpAction)) {\n return {\n content: `Action \"${String(action)}\" is not allowed through ${name}`,\n isError: true,\n };\n }\n\n try {\n const result = await executeKanban(args, context, new AbortController().signal);\n const failed =\n !!result &&\n typeof result === 'object' &&\n !Array.isArray(result) &&\n (result as Record<string, unknown>)['ok'] === false;\n return { content: result, isError: failed };\n } catch (error) {\n return {\n content: error instanceof Error ? error.message : String(error),\n isError: true,\n };\n }\n },\n };\n}\n\nexport function createKanbanMcpServer(\n projectRoot: string,\n opts: KanbanMcpToolHostOptions = {},\n): MCPServer {\n return new MCPServer({\n host: createKanbanMcpToolHost(projectRoot, opts),\n serverInfo: { name: 'wrongstack-kanban-mcp', version: SERVER_INFO.version },\n prompts: [\n {\n name: 'work-kanban-task',\n title: 'Work a WrongStack Kanban task',\n description:\n 'Inspect, claim, execute, verify, and update a task without bypassing board policy.',\n arguments: [{ name: 'boardId', description: 'Board id or unique prefix', required: true }],\n template:\n 'Use the WrongStack Kanban MCP tools on board {{boardId}}. Re-read authoritative board state before mutation, claim an eligible task, keep assignment state current, attach truthful evidence, and satisfy its completion gate.',\n },\n ],\n });\n}\n", "export const KANBAN_READ_ACTIONS = [\n 'list_boards',\n 'get_board',\n 'export_markdown',\n 'export_task_graph',\n 'search_tasks',\n 'ready_tasks',\n 'snapshot',\n 'get_task',\n 'get_chain',\n 'events',\n 'queue_health',\n] as const;\n\nexport const KANBAN_MANAGE_ACTIONS = [\n 'create_board',\n 'duplicate_board',\n 'update_board',\n 'adopt_managed_lifecycle',\n 'generate_board',\n 'sync_task_graph',\n 'create_from_graph',\n 'import_session_tasks',\n 'add_column',\n 'update_column',\n 'add_task',\n 'split_task',\n 'copy_task',\n 'update_task',\n 'transition_task',\n 'repair_managed_projection',\n 'move_task',\n 'set_chain',\n 'claim_task',\n 'release_task',\n 'assign_task',\n 'mark_assignment',\n 'heartbeat_assignment',\n 'recover_stale',\n 'add_dependency',\n 'add_goal_metric',\n 'update_goal_metric',\n 'add_check',\n 'update_check',\n 'add_note',\n 'add_link',\n 'verify_completion',\n 'split_atomic',\n 'assess_atomicity',\n 'propose_decomposition',\n] as const;\n\n/** Operations that remove or absorb durable task/board state. */\nexport const KANBAN_DESTRUCTIVE_ACTIONS = [\n 'delete_board',\n 'delete_column',\n 'delete_task',\n 'merge_tasks',\n 'transfer_task',\n] as const;\n\nexport type KanbanReadAction = (typeof KANBAN_READ_ACTIONS)[number];\nexport type KanbanManageAction = (typeof KANBAN_MANAGE_ACTIONS)[number];\nexport type KanbanDestructiveAction = (typeof KANBAN_DESTRUCTIVE_ACTIONS)[number];\nexport type KanbanMcpAction = KanbanReadAction | KanbanManageAction | KanbanDestructiveAction;\nexport type KanbanMcpToolName =\n | 'kanban_read'\n | 'kanban_manage'\n | 'kanban_destructive'\n | 'kanban_watch';\n\nexport interface KanbanMcpPolicyOptions {\n writable?: boolean;\n destructive?: boolean;\n}\n\nexport interface KanbanMcpToolPolicy {\n name: KanbanMcpToolName;\n actions?: readonly KanbanMcpAction[];\n}\n\nexport function selectKanbanMcpTools(opts: KanbanMcpPolicyOptions = {}): KanbanMcpToolPolicy[] {\n const tools: KanbanMcpToolPolicy[] = [\n { name: 'kanban_read', actions: KANBAN_READ_ACTIONS },\n { name: 'kanban_watch' },\n ];\n if (opts.writable === true || opts.destructive === true) {\n tools.push({ name: 'kanban_manage', actions: KANBAN_MANAGE_ACTIONS });\n }\n if (opts.destructive === true) {\n tools.push({ name: 'kanban_destructive', actions: KANBAN_DESTRUCTIVE_ACTIONS });\n }\n return tools;\n}\n", "import { readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst here = dirname(fileURLToPath(import.meta.url));\nconst packagePath = resolve(here, '..', 'package.json');\n\ninterface MinimalPackage {\n name?: string;\n version?: string;\n}\n\nlet cached: { name: string; version: string } | undefined;\n\nfunction readServerInfo(): { name: string; version: string } {\n if (cached) return cached;\n try {\n const pkg = JSON.parse(readFileSync(packagePath, 'utf8')) as MinimalPackage;\n cached = {\n name: pkg.name ?? '@wrongstack/kanban-mcp',\n version: pkg.version ?? '0.0.0',\n };\n } catch {\n cached = { name: '@wrongstack/kanban-mcp', version: '0.0.0' };\n }\n return cached;\n}\n\nexport const SERVER_INFO = readServerInfo();\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["fileURLToPath", "getKanbanServerConnection", "resolve", "getKanbanServerConnection", "handle", "resolve", "fileURLToPath"]
|
|
7
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createKanbanMcpServer, createKanbanMcpToolHost, type KanbanMcpDependencies, type KanbanMcpToolHostOptions, } from './adapter.js';
|
|
2
|
+
export { KANBAN_DESTRUCTIVE_ACTIONS, KANBAN_MANAGE_ACTIONS, KANBAN_READ_ACTIONS, type KanbanDestructiveAction, type KanbanManageAction, type KanbanMcpAction, type KanbanMcpPolicyOptions, type KanbanMcpToolName, type KanbanReadAction, selectKanbanMcpTools, } from './policy.js';
|
|
3
|
+
export { SERVER_INFO } from './version.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,GAC9B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,mBAAmB,EACnB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// src/adapter.ts
|
|
2
|
+
import { getKanbanServerConnection } from "@wrongstack/kanban";
|
|
3
|
+
import {
|
|
4
|
+
MCPServer
|
|
5
|
+
} from "@wrongstack/mcp";
|
|
6
|
+
import { kanbanTool } from "@wrongstack/tools/kanban";
|
|
7
|
+
|
|
8
|
+
// src/policy.ts
|
|
9
|
+
var KANBAN_READ_ACTIONS = [
|
|
10
|
+
"list_boards",
|
|
11
|
+
"get_board",
|
|
12
|
+
"export_markdown",
|
|
13
|
+
"export_task_graph",
|
|
14
|
+
"search_tasks",
|
|
15
|
+
"ready_tasks",
|
|
16
|
+
"snapshot",
|
|
17
|
+
"get_task",
|
|
18
|
+
"get_chain",
|
|
19
|
+
"events",
|
|
20
|
+
"queue_health"
|
|
21
|
+
];
|
|
22
|
+
var KANBAN_MANAGE_ACTIONS = [
|
|
23
|
+
"create_board",
|
|
24
|
+
"duplicate_board",
|
|
25
|
+
"update_board",
|
|
26
|
+
"adopt_managed_lifecycle",
|
|
27
|
+
"generate_board",
|
|
28
|
+
"sync_task_graph",
|
|
29
|
+
"create_from_graph",
|
|
30
|
+
"import_session_tasks",
|
|
31
|
+
"add_column",
|
|
32
|
+
"update_column",
|
|
33
|
+
"add_task",
|
|
34
|
+
"split_task",
|
|
35
|
+
"copy_task",
|
|
36
|
+
"update_task",
|
|
37
|
+
"transition_task",
|
|
38
|
+
"repair_managed_projection",
|
|
39
|
+
"move_task",
|
|
40
|
+
"set_chain",
|
|
41
|
+
"claim_task",
|
|
42
|
+
"release_task",
|
|
43
|
+
"assign_task",
|
|
44
|
+
"mark_assignment",
|
|
45
|
+
"heartbeat_assignment",
|
|
46
|
+
"recover_stale",
|
|
47
|
+
"add_dependency",
|
|
48
|
+
"add_goal_metric",
|
|
49
|
+
"update_goal_metric",
|
|
50
|
+
"add_check",
|
|
51
|
+
"update_check",
|
|
52
|
+
"add_note",
|
|
53
|
+
"add_link",
|
|
54
|
+
"verify_completion",
|
|
55
|
+
"split_atomic",
|
|
56
|
+
"assess_atomicity",
|
|
57
|
+
"propose_decomposition"
|
|
58
|
+
];
|
|
59
|
+
var KANBAN_DESTRUCTIVE_ACTIONS = [
|
|
60
|
+
"delete_board",
|
|
61
|
+
"delete_column",
|
|
62
|
+
"delete_task",
|
|
63
|
+
"merge_tasks",
|
|
64
|
+
"transfer_task"
|
|
65
|
+
];
|
|
66
|
+
function selectKanbanMcpTools(opts = {}) {
|
|
67
|
+
const tools = [
|
|
68
|
+
{ name: "kanban_read", actions: KANBAN_READ_ACTIONS },
|
|
69
|
+
{ name: "kanban_watch" }
|
|
70
|
+
];
|
|
71
|
+
if (opts.writable === true || opts.destructive === true) {
|
|
72
|
+
tools.push({ name: "kanban_manage", actions: KANBAN_MANAGE_ACTIONS });
|
|
73
|
+
}
|
|
74
|
+
if (opts.destructive === true) {
|
|
75
|
+
tools.push({ name: "kanban_destructive", actions: KANBAN_DESTRUCTIVE_ACTIONS });
|
|
76
|
+
}
|
|
77
|
+
return tools;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/version.ts
|
|
81
|
+
import { readFileSync } from "node:fs";
|
|
82
|
+
import { dirname, resolve } from "node:path";
|
|
83
|
+
import { fileURLToPath } from "node:url";
|
|
84
|
+
var here = dirname(fileURLToPath(import.meta.url));
|
|
85
|
+
var packagePath = resolve(here, "..", "package.json");
|
|
86
|
+
var cached;
|
|
87
|
+
function readServerInfo() {
|
|
88
|
+
if (cached) return cached;
|
|
89
|
+
try {
|
|
90
|
+
const pkg = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
91
|
+
cached = {
|
|
92
|
+
name: pkg.name ?? "@wrongstack/kanban-mcp",
|
|
93
|
+
version: pkg.version ?? "0.0.0"
|
|
94
|
+
};
|
|
95
|
+
} catch {
|
|
96
|
+
cached = { name: "@wrongstack/kanban-mcp", version: "0.0.0" };
|
|
97
|
+
}
|
|
98
|
+
return cached;
|
|
99
|
+
}
|
|
100
|
+
var SERVER_INFO = readServerInfo();
|
|
101
|
+
|
|
102
|
+
// src/adapter.ts
|
|
103
|
+
var WATCH_DEFAULT_TIMEOUT_MS = 15e3;
|
|
104
|
+
var WATCH_MAX_TIMEOUT_MS = 25e3;
|
|
105
|
+
function actionSchema(actions) {
|
|
106
|
+
const schema = structuredClone(kanbanTool.inputSchema);
|
|
107
|
+
const properties = schema["properties"];
|
|
108
|
+
if (!properties || typeof properties !== "object" || Array.isArray(properties)) {
|
|
109
|
+
throw new Error("Kanban MCP: Kanban tool schema has no properties object");
|
|
110
|
+
}
|
|
111
|
+
const action = properties["action"];
|
|
112
|
+
if (!action || typeof action !== "object" || Array.isArray(action)) {
|
|
113
|
+
throw new Error("Kanban MCP: Kanban tool schema has no action property");
|
|
114
|
+
}
|
|
115
|
+
action["enum"] = [...actions];
|
|
116
|
+
return schema;
|
|
117
|
+
}
|
|
118
|
+
var WATCH_SCHEMA = {
|
|
119
|
+
type: "object",
|
|
120
|
+
properties: {
|
|
121
|
+
boardId: {
|
|
122
|
+
type: "string",
|
|
123
|
+
description: "Optional full board id. Omit to observe any board."
|
|
124
|
+
},
|
|
125
|
+
timeoutMs: {
|
|
126
|
+
type: "number",
|
|
127
|
+
minimum: 100,
|
|
128
|
+
maximum: WATCH_MAX_TIMEOUT_MS,
|
|
129
|
+
description: `Long-poll timeout. Defaults to ${WATCH_DEFAULT_TIMEOUT_MS}.`
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
additionalProperties: false
|
|
133
|
+
};
|
|
134
|
+
var TOOL_DESCRIPTIONS = {
|
|
135
|
+
kanban_read: "Read authoritative WrongStack Kanban boards, tasks, events, queue health, task chains, and exports through the project IPC owner.",
|
|
136
|
+
kanban_manage: "Create and update WrongStack Kanban boards and tasks, assignments, dependencies, evidence, verification, and orchestration state. Requires server --writable.",
|
|
137
|
+
kanban_destructive: "Delete or absorb durable WrongStack Kanban state, including board/task/column deletion, merge, and cross-board transfer. Requires server --destructive.",
|
|
138
|
+
kanban_watch: "Wait for the next project Kanban mutation event. After any event or timeout, reconcile authoritative state with kanban_read."
|
|
139
|
+
};
|
|
140
|
+
function toolDescriptor(name, actions) {
|
|
141
|
+
return {
|
|
142
|
+
name,
|
|
143
|
+
description: TOOL_DESCRIPTIONS[name],
|
|
144
|
+
inputSchema: actions ? actionSchema(actions) : WATCH_SCHEMA
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function createContext(projectRoot, actor) {
|
|
148
|
+
return {
|
|
149
|
+
systemPrompt: [],
|
|
150
|
+
cwd: projectRoot,
|
|
151
|
+
projectRoot,
|
|
152
|
+
allowOutsideProjectRoot: false,
|
|
153
|
+
model: "kanban-mcp",
|
|
154
|
+
tools: [],
|
|
155
|
+
meta: { source: "kanban-mcp" },
|
|
156
|
+
agentId: actor,
|
|
157
|
+
agentName: actor
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function normalizeTimeout(value) {
|
|
161
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return WATCH_DEFAULT_TIMEOUT_MS;
|
|
162
|
+
return Math.min(WATCH_MAX_TIMEOUT_MS, Math.max(100, Math.trunc(value)));
|
|
163
|
+
}
|
|
164
|
+
function eventBoardId(event) {
|
|
165
|
+
if (!event.data || typeof event.data !== "object" || Array.isArray(event.data)) return void 0;
|
|
166
|
+
const boardId = event.data["boardId"];
|
|
167
|
+
return typeof boardId === "string" ? boardId : void 0;
|
|
168
|
+
}
|
|
169
|
+
async function watchForKanbanMutation(projectRoot, args, getConnection) {
|
|
170
|
+
const boardId = typeof args["boardId"] === "string" ? args["boardId"] : void 0;
|
|
171
|
+
const timeoutMs = normalizeTimeout(args["timeoutMs"]);
|
|
172
|
+
const connection = await getConnection(projectRoot);
|
|
173
|
+
if (!connection) {
|
|
174
|
+
throw new Error("Kanban project server is disabled; live watch is unavailable");
|
|
175
|
+
}
|
|
176
|
+
return await new Promise((resolve2) => {
|
|
177
|
+
let settled = false;
|
|
178
|
+
let unsubscribeEvent = () => {
|
|
179
|
+
};
|
|
180
|
+
let unsubscribeDisconnect = () => {
|
|
181
|
+
};
|
|
182
|
+
let timer;
|
|
183
|
+
const finish = (result) => {
|
|
184
|
+
if (settled) return;
|
|
185
|
+
settled = true;
|
|
186
|
+
if (timer) clearTimeout(timer);
|
|
187
|
+
unsubscribeEvent();
|
|
188
|
+
unsubscribeDisconnect();
|
|
189
|
+
resolve2(result);
|
|
190
|
+
};
|
|
191
|
+
unsubscribeEvent = connection.subscribe((event) => {
|
|
192
|
+
const changedBoardId = eventBoardId(event);
|
|
193
|
+
if (boardId && changedBoardId !== boardId) return;
|
|
194
|
+
finish({ changed: true, event: event.event, data: event.data });
|
|
195
|
+
});
|
|
196
|
+
unsubscribeDisconnect = connection.onDisconnect(() => {
|
|
197
|
+
finish({ changed: false, disconnected: true, reason: "Kanban daemon disconnected" });
|
|
198
|
+
});
|
|
199
|
+
timer = setTimeout(() => {
|
|
200
|
+
finish({ changed: false, timedOut: true, timeoutMs });
|
|
201
|
+
}, timeoutMs);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function createKanbanMcpToolHost(projectRoot, opts = {}) {
|
|
205
|
+
const policy = selectKanbanMcpTools(opts);
|
|
206
|
+
const allowed = new Map(policy.map((entry) => [entry.name, entry.actions]));
|
|
207
|
+
const actor = opts.actor?.trim() || "external-kanban-mcp";
|
|
208
|
+
const context = createContext(projectRoot, actor);
|
|
209
|
+
const executeKanban = opts.dependencies?.executeKanban ?? (async (args, ctx, signal) => await kanbanTool.execute(args, ctx, { signal }));
|
|
210
|
+
const getConnection = opts.dependencies?.getConnection ?? getKanbanServerConnection;
|
|
211
|
+
return {
|
|
212
|
+
listTools() {
|
|
213
|
+
return policy.map((entry) => toolDescriptor(entry.name, entry.actions));
|
|
214
|
+
},
|
|
215
|
+
async callTool(name, args) {
|
|
216
|
+
if (!allowed.has(name)) {
|
|
217
|
+
return {
|
|
218
|
+
content: `Tool "${name}" is not exposed by this Kanban MCP server`,
|
|
219
|
+
isError: true
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (name === "kanban_watch") {
|
|
223
|
+
try {
|
|
224
|
+
const content = await watchForKanbanMutation(projectRoot, args, getConnection);
|
|
225
|
+
return { content, isError: false };
|
|
226
|
+
} catch (error) {
|
|
227
|
+
return {
|
|
228
|
+
content: error instanceof Error ? error.message : String(error),
|
|
229
|
+
isError: true
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const actions = allowed.get(name);
|
|
234
|
+
const action = args["action"];
|
|
235
|
+
if (typeof action !== "string" || !actions?.includes(action)) {
|
|
236
|
+
return {
|
|
237
|
+
content: `Action "${String(action)}" is not allowed through ${name}`,
|
|
238
|
+
isError: true
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
const result = await executeKanban(args, context, new AbortController().signal);
|
|
243
|
+
const failed = !!result && typeof result === "object" && !Array.isArray(result) && result["ok"] === false;
|
|
244
|
+
return { content: result, isError: failed };
|
|
245
|
+
} catch (error) {
|
|
246
|
+
return {
|
|
247
|
+
content: error instanceof Error ? error.message : String(error),
|
|
248
|
+
isError: true
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
function createKanbanMcpServer(projectRoot, opts = {}) {
|
|
255
|
+
return new MCPServer({
|
|
256
|
+
host: createKanbanMcpToolHost(projectRoot, opts),
|
|
257
|
+
serverInfo: { name: "wrongstack-kanban-mcp", version: SERVER_INFO.version },
|
|
258
|
+
prompts: [
|
|
259
|
+
{
|
|
260
|
+
name: "work-kanban-task",
|
|
261
|
+
title: "Work a WrongStack Kanban task",
|
|
262
|
+
description: "Inspect, claim, execute, verify, and update a task without bypassing board policy.",
|
|
263
|
+
arguments: [{ name: "boardId", description: "Board id or unique prefix", required: true }],
|
|
264
|
+
template: "Use the WrongStack Kanban MCP tools on board {{boardId}}. Re-read authoritative board state before mutation, claim an eligible task, keep assignment state current, attach truthful evidence, and satisfy its completion gate."
|
|
265
|
+
}
|
|
266
|
+
]
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
export {
|
|
270
|
+
KANBAN_DESTRUCTIVE_ACTIONS,
|
|
271
|
+
KANBAN_MANAGE_ACTIONS,
|
|
272
|
+
KANBAN_READ_ACTIONS,
|
|
273
|
+
SERVER_INFO,
|
|
274
|
+
createKanbanMcpServer,
|
|
275
|
+
createKanbanMcpToolHost,
|
|
276
|
+
selectKanbanMcpTools
|
|
277
|
+
};
|
|
278
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/adapter.ts", "../src/policy.ts", "../src/version.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@wrongstack/core/agent';\nimport { getKanbanServerConnection, type KanbanServerEvent } from '@wrongstack/kanban';\nimport {\n MCPServer,\n type MCPServerCallResult,\n type MCPServerTool,\n type MCPServerToolHost,\n} from '@wrongstack/mcp';\nimport { kanbanTool } from '@wrongstack/tools/kanban';\nimport {\n type KanbanMcpAction,\n type KanbanMcpPolicyOptions,\n type KanbanMcpToolName,\n selectKanbanMcpTools,\n} from './policy.js';\nimport { SERVER_INFO } from './version.js';\n\nconst WATCH_DEFAULT_TIMEOUT_MS = 15_000;\nconst WATCH_MAX_TIMEOUT_MS = 25_000;\n\ninterface KanbanEventConnection {\n subscribe(listener: (event: KanbanServerEvent) => void): () => void;\n onDisconnect(listener: () => void): () => void;\n}\n\nexport interface KanbanMcpDependencies {\n executeKanban?: (\n args: Record<string, unknown>,\n context: Context,\n signal: AbortSignal,\n ) => Promise<unknown>;\n getConnection?: (projectRoot: string) => Promise<KanbanEventConnection | null>;\n}\n\nexport interface KanbanMcpToolHostOptions extends KanbanMcpPolicyOptions {\n actor?: string | undefined;\n dependencies?: KanbanMcpDependencies | undefined;\n}\n\nfunction actionSchema(actions: readonly KanbanMcpAction[]): Record<string, unknown> {\n const schema = structuredClone(kanbanTool.inputSchema) as Record<string, unknown>;\n const properties = schema['properties'];\n if (!properties || typeof properties !== 'object' || Array.isArray(properties)) {\n throw new Error('Kanban MCP: Kanban tool schema has no properties object');\n }\n const action = (properties as Record<string, unknown>)['action'];\n if (!action || typeof action !== 'object' || Array.isArray(action)) {\n throw new Error('Kanban MCP: Kanban tool schema has no action property');\n }\n (action as Record<string, unknown>)['enum'] = [...actions];\n return schema;\n}\n\nconst WATCH_SCHEMA: Record<string, unknown> = {\n type: 'object',\n properties: {\n boardId: {\n type: 'string',\n description: 'Optional full board id. Omit to observe any board.',\n },\n timeoutMs: {\n type: 'number',\n minimum: 100,\n maximum: WATCH_MAX_TIMEOUT_MS,\n description: `Long-poll timeout. Defaults to ${WATCH_DEFAULT_TIMEOUT_MS}.`,\n },\n },\n additionalProperties: false,\n};\n\nconst TOOL_DESCRIPTIONS: Record<KanbanMcpToolName, string> = {\n kanban_read:\n 'Read authoritative WrongStack Kanban boards, tasks, events, queue health, task chains, and exports through the project IPC owner.',\n kanban_manage:\n 'Create and update WrongStack Kanban boards and tasks, assignments, dependencies, evidence, verification, and orchestration state. Requires server --writable.',\n kanban_destructive:\n 'Delete or absorb durable WrongStack Kanban state, including board/task/column deletion, merge, and cross-board transfer. Requires server --destructive.',\n kanban_watch:\n 'Wait for the next project Kanban mutation event. After any event or timeout, reconcile authoritative state with kanban_read.',\n};\n\nfunction toolDescriptor(\n name: KanbanMcpToolName,\n actions?: readonly KanbanMcpAction[],\n): MCPServerTool {\n return {\n name,\n description: TOOL_DESCRIPTIONS[name],\n inputSchema: actions ? actionSchema(actions) : WATCH_SCHEMA,\n };\n}\n\nfunction createContext(projectRoot: string, actor: string): Context {\n return {\n systemPrompt: [],\n cwd: projectRoot,\n projectRoot,\n allowOutsideProjectRoot: false,\n model: 'kanban-mcp',\n tools: [],\n meta: { source: 'kanban-mcp' },\n agentId: actor,\n agentName: actor,\n } as unknown as Context;\n}\n\nfunction normalizeTimeout(value: unknown): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return WATCH_DEFAULT_TIMEOUT_MS;\n return Math.min(WATCH_MAX_TIMEOUT_MS, Math.max(100, Math.trunc(value)));\n}\n\nfunction eventBoardId(event: KanbanServerEvent): string | undefined {\n if (!event.data || typeof event.data !== 'object' || Array.isArray(event.data)) return undefined;\n const boardId = (event.data as Record<string, unknown>)['boardId'];\n return typeof boardId === 'string' ? boardId : undefined;\n}\n\nasync function watchForKanbanMutation(\n projectRoot: string,\n args: Record<string, unknown>,\n getConnection: (projectRoot: string) => Promise<KanbanEventConnection | null>,\n): Promise<unknown> {\n const boardId = typeof args['boardId'] === 'string' ? args['boardId'] : undefined;\n const timeoutMs = normalizeTimeout(args['timeoutMs']);\n const connection = await getConnection(projectRoot);\n if (!connection) {\n throw new Error('Kanban project server is disabled; live watch is unavailable');\n }\n\n return await new Promise((resolve) => {\n let settled = false;\n let unsubscribeEvent = (): void => {};\n let unsubscribeDisconnect = (): void => {};\n let timer: NodeJS.Timeout | undefined;\n\n const finish = (result: unknown): void => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n unsubscribeEvent();\n unsubscribeDisconnect();\n resolve(result);\n };\n\n unsubscribeEvent = connection.subscribe((event) => {\n const changedBoardId = eventBoardId(event);\n if (boardId && changedBoardId !== boardId) return;\n finish({ changed: true, event: event.event, data: event.data });\n });\n unsubscribeDisconnect = connection.onDisconnect(() => {\n finish({ changed: false, disconnected: true, reason: 'Kanban daemon disconnected' });\n });\n timer = setTimeout(() => {\n finish({ changed: false, timedOut: true, timeoutMs });\n }, timeoutMs);\n });\n}\n\nexport function createKanbanMcpToolHost(\n projectRoot: string,\n opts: KanbanMcpToolHostOptions = {},\n): MCPServerToolHost {\n const policy = selectKanbanMcpTools(opts);\n const allowed = new Map(policy.map((entry) => [entry.name, entry.actions]));\n const actor = opts.actor?.trim() || 'external-kanban-mcp';\n const context = createContext(projectRoot, actor);\n const executeKanban =\n opts.dependencies?.executeKanban ??\n (async (args: Record<string, unknown>, ctx: Context, signal: AbortSignal) =>\n await kanbanTool.execute(args as never, ctx, { signal }));\n const getConnection = opts.dependencies?.getConnection ?? getKanbanServerConnection;\n\n return {\n listTools(): MCPServerTool[] {\n return policy.map((entry) => toolDescriptor(entry.name, entry.actions));\n },\n\n async callTool(name: string, args: Record<string, unknown>): Promise<MCPServerCallResult> {\n if (!allowed.has(name as KanbanMcpToolName)) {\n return {\n content: `Tool \"${name}\" is not exposed by this Kanban MCP server`,\n isError: true,\n };\n }\n\n if (name === 'kanban_watch') {\n try {\n const content = await watchForKanbanMutation(projectRoot, args, getConnection);\n return { content, isError: false };\n } catch (error) {\n return {\n content: error instanceof Error ? error.message : String(error),\n isError: true,\n };\n }\n }\n\n const actions = allowed.get(name as KanbanMcpToolName);\n const action = args['action'];\n if (typeof action !== 'string' || !actions?.includes(action as KanbanMcpAction)) {\n return {\n content: `Action \"${String(action)}\" is not allowed through ${name}`,\n isError: true,\n };\n }\n\n try {\n const result = await executeKanban(args, context, new AbortController().signal);\n const failed =\n !!result &&\n typeof result === 'object' &&\n !Array.isArray(result) &&\n (result as Record<string, unknown>)['ok'] === false;\n return { content: result, isError: failed };\n } catch (error) {\n return {\n content: error instanceof Error ? error.message : String(error),\n isError: true,\n };\n }\n },\n };\n}\n\nexport function createKanbanMcpServer(\n projectRoot: string,\n opts: KanbanMcpToolHostOptions = {},\n): MCPServer {\n return new MCPServer({\n host: createKanbanMcpToolHost(projectRoot, opts),\n serverInfo: { name: 'wrongstack-kanban-mcp', version: SERVER_INFO.version },\n prompts: [\n {\n name: 'work-kanban-task',\n title: 'Work a WrongStack Kanban task',\n description:\n 'Inspect, claim, execute, verify, and update a task without bypassing board policy.',\n arguments: [{ name: 'boardId', description: 'Board id or unique prefix', required: true }],\n template:\n 'Use the WrongStack Kanban MCP tools on board {{boardId}}. Re-read authoritative board state before mutation, claim an eligible task, keep assignment state current, attach truthful evidence, and satisfy its completion gate.',\n },\n ],\n });\n}\n", "export const KANBAN_READ_ACTIONS = [\n 'list_boards',\n 'get_board',\n 'export_markdown',\n 'export_task_graph',\n 'search_tasks',\n 'ready_tasks',\n 'snapshot',\n 'get_task',\n 'get_chain',\n 'events',\n 'queue_health',\n] as const;\n\nexport const KANBAN_MANAGE_ACTIONS = [\n 'create_board',\n 'duplicate_board',\n 'update_board',\n 'adopt_managed_lifecycle',\n 'generate_board',\n 'sync_task_graph',\n 'create_from_graph',\n 'import_session_tasks',\n 'add_column',\n 'update_column',\n 'add_task',\n 'split_task',\n 'copy_task',\n 'update_task',\n 'transition_task',\n 'repair_managed_projection',\n 'move_task',\n 'set_chain',\n 'claim_task',\n 'release_task',\n 'assign_task',\n 'mark_assignment',\n 'heartbeat_assignment',\n 'recover_stale',\n 'add_dependency',\n 'add_goal_metric',\n 'update_goal_metric',\n 'add_check',\n 'update_check',\n 'add_note',\n 'add_link',\n 'verify_completion',\n 'split_atomic',\n 'assess_atomicity',\n 'propose_decomposition',\n] as const;\n\n/** Operations that remove or absorb durable task/board state. */\nexport const KANBAN_DESTRUCTIVE_ACTIONS = [\n 'delete_board',\n 'delete_column',\n 'delete_task',\n 'merge_tasks',\n 'transfer_task',\n] as const;\n\nexport type KanbanReadAction = (typeof KANBAN_READ_ACTIONS)[number];\nexport type KanbanManageAction = (typeof KANBAN_MANAGE_ACTIONS)[number];\nexport type KanbanDestructiveAction = (typeof KANBAN_DESTRUCTIVE_ACTIONS)[number];\nexport type KanbanMcpAction = KanbanReadAction | KanbanManageAction | KanbanDestructiveAction;\nexport type KanbanMcpToolName =\n | 'kanban_read'\n | 'kanban_manage'\n | 'kanban_destructive'\n | 'kanban_watch';\n\nexport interface KanbanMcpPolicyOptions {\n writable?: boolean;\n destructive?: boolean;\n}\n\nexport interface KanbanMcpToolPolicy {\n name: KanbanMcpToolName;\n actions?: readonly KanbanMcpAction[];\n}\n\nexport function selectKanbanMcpTools(opts: KanbanMcpPolicyOptions = {}): KanbanMcpToolPolicy[] {\n const tools: KanbanMcpToolPolicy[] = [\n { name: 'kanban_read', actions: KANBAN_READ_ACTIONS },\n { name: 'kanban_watch' },\n ];\n if (opts.writable === true || opts.destructive === true) {\n tools.push({ name: 'kanban_manage', actions: KANBAN_MANAGE_ACTIONS });\n }\n if (opts.destructive === true) {\n tools.push({ name: 'kanban_destructive', actions: KANBAN_DESTRUCTIVE_ACTIONS });\n }\n return tools;\n}\n", "import { readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst here = dirname(fileURLToPath(import.meta.url));\nconst packagePath = resolve(here, '..', 'package.json');\n\ninterface MinimalPackage {\n name?: string;\n version?: string;\n}\n\nlet cached: { name: string; version: string } | undefined;\n\nfunction readServerInfo(): { name: string; version: string } {\n if (cached) return cached;\n try {\n const pkg = JSON.parse(readFileSync(packagePath, 'utf8')) as MinimalPackage;\n cached = {\n name: pkg.name ?? '@wrongstack/kanban-mcp',\n version: pkg.version ?? '0.0.0',\n };\n } catch {\n cached = { name: '@wrongstack/kanban-mcp', version: '0.0.0' };\n }\n return cached;\n}\n\nexport const SERVER_INFO = readServerInfo();\n"],
|
|
5
|
+
"mappings": ";AACA,SAAS,iCAAyD;AAClE;AAAA,EACE;AAAA,OAIK;AACP,SAAS,kBAAkB;;;ACRpB,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,wBAAwB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAsBO,SAAS,qBAAqB,OAA+B,CAAC,GAA0B;AAC7F,QAAM,QAA+B;AAAA,IACnC,EAAE,MAAM,eAAe,SAAS,oBAAoB;AAAA,IACpD,EAAE,MAAM,eAAe;AAAA,EACzB;AACA,MAAI,KAAK,aAAa,QAAQ,KAAK,gBAAgB,MAAM;AACvD,UAAM,KAAK,EAAE,MAAM,iBAAiB,SAAS,sBAAsB,CAAC;AAAA,EACtE;AACA,MAAI,KAAK,gBAAgB,MAAM;AAC7B,UAAM,KAAK,EAAE,MAAM,sBAAsB,SAAS,2BAA2B,CAAC;AAAA,EAChF;AACA,SAAO;AACT;;;AC7FA,SAAS,oBAAoB;AAC7B,SAAS,SAAS,eAAe;AACjC,SAAS,qBAAqB;AAE9B,IAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AACnD,IAAM,cAAc,QAAQ,MAAM,MAAM,cAAc;AAOtD,IAAI;AAEJ,SAAS,iBAAoD;AAC3D,MAAI,OAAQ,QAAO;AACnB,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,aAAa,aAAa,MAAM,CAAC;AACxD,aAAS;AAAA,MACP,MAAM,IAAI,QAAQ;AAAA,MAClB,SAAS,IAAI,WAAW;AAAA,IAC1B;AAAA,EACF,QAAQ;AACN,aAAS,EAAE,MAAM,0BAA0B,SAAS,QAAQ;AAAA,EAC9D;AACA,SAAO;AACT;AAEO,IAAM,cAAc,eAAe;;;AFX1C,IAAM,2BAA2B;AACjC,IAAM,uBAAuB;AAqB7B,SAAS,aAAa,SAA8D;AAClF,QAAM,SAAS,gBAAgB,WAAW,WAAW;AACrD,QAAM,aAAa,OAAO,YAAY;AACtC,MAAI,CAAC,cAAc,OAAO,eAAe,YAAY,MAAM,QAAQ,UAAU,GAAG;AAC9E,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,QAAM,SAAU,WAAuC,QAAQ;AAC/D,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAClE,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE;AACA,EAAC,OAAmC,MAAM,IAAI,CAAC,GAAG,OAAO;AACzD,SAAO;AACT;AAEA,IAAM,eAAwC;AAAA,EAC5C,MAAM;AAAA,EACN,YAAY;AAAA,IACV,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,WAAW;AAAA,MACT,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,MACT,aAAa,kCAAkC,wBAAwB;AAAA,IACzE;AAAA,EACF;AAAA,EACA,sBAAsB;AACxB;AAEA,IAAM,oBAAuD;AAAA,EAC3D,aACE;AAAA,EACF,eACE;AAAA,EACF,oBACE;AAAA,EACF,cACE;AACJ;AAEA,SAAS,eACP,MACA,SACe;AACf,SAAO;AAAA,IACL;AAAA,IACA,aAAa,kBAAkB,IAAI;AAAA,IACnC,aAAa,UAAU,aAAa,OAAO,IAAI;AAAA,EACjD;AACF;AAEA,SAAS,cAAc,aAAqB,OAAwB;AAClE,SAAO;AAAA,IACL,cAAc,CAAC;AAAA,IACf,KAAK;AAAA,IACL;AAAA,IACA,yBAAyB;AAAA,IACzB,OAAO;AAAA,IACP,OAAO,CAAC;AAAA,IACR,MAAM,EAAE,QAAQ,aAAa;AAAA,IAC7B,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AACF;AAEA,SAAS,iBAAiB,OAAwB;AAChD,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,SAAO,KAAK,IAAI,sBAAsB,KAAK,IAAI,KAAK,KAAK,MAAM,KAAK,CAAC,CAAC;AACxE;AAEA,SAAS,aAAa,OAA8C;AAClE,MAAI,CAAC,MAAM,QAAQ,OAAO,MAAM,SAAS,YAAY,MAAM,QAAQ,MAAM,IAAI,EAAG,QAAO;AACvF,QAAM,UAAW,MAAM,KAAiC,SAAS;AACjE,SAAO,OAAO,YAAY,WAAW,UAAU;AACjD;AAEA,eAAe,uBACb,aACA,MACA,eACkB;AAClB,QAAM,UAAU,OAAO,KAAK,SAAS,MAAM,WAAW,KAAK,SAAS,IAAI;AACxE,QAAM,YAAY,iBAAiB,KAAK,WAAW,CAAC;AACpD,QAAM,aAAa,MAAM,cAAc,WAAW;AAClD,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,8DAA8D;AAAA,EAChF;AAEA,SAAO,MAAM,IAAI,QAAQ,CAACA,aAAY;AACpC,QAAI,UAAU;AACd,QAAI,mBAAmB,MAAY;AAAA,IAAC;AACpC,QAAI,wBAAwB,MAAY;AAAA,IAAC;AACzC,QAAI;AAEJ,UAAM,SAAS,CAAC,WAA0B;AACxC,UAAI,QAAS;AACb,gBAAU;AACV,UAAI,MAAO,cAAa,KAAK;AAC7B,uBAAiB;AACjB,4BAAsB;AACtB,MAAAA,SAAQ,MAAM;AAAA,IAChB;AAEA,uBAAmB,WAAW,UAAU,CAAC,UAAU;AACjD,YAAM,iBAAiB,aAAa,KAAK;AACzC,UAAI,WAAW,mBAAmB,QAAS;AAC3C,aAAO,EAAE,SAAS,MAAM,OAAO,MAAM,OAAO,MAAM,MAAM,KAAK,CAAC;AAAA,IAChE,CAAC;AACD,4BAAwB,WAAW,aAAa,MAAM;AACpD,aAAO,EAAE,SAAS,OAAO,cAAc,MAAM,QAAQ,6BAA6B,CAAC;AAAA,IACrF,CAAC;AACD,YAAQ,WAAW,MAAM;AACvB,aAAO,EAAE,SAAS,OAAO,UAAU,MAAM,UAAU,CAAC;AAAA,IACtD,GAAG,SAAS;AAAA,EACd,CAAC;AACH;AAEO,SAAS,wBACd,aACA,OAAiC,CAAC,GACf;AACnB,QAAM,SAAS,qBAAqB,IAAI;AACxC,QAAM,UAAU,IAAI,IAAI,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,MAAM,MAAM,OAAO,CAAC,CAAC;AAC1E,QAAM,QAAQ,KAAK,OAAO,KAAK,KAAK;AACpC,QAAM,UAAU,cAAc,aAAa,KAAK;AAChD,QAAM,gBACJ,KAAK,cAAc,kBAClB,OAAO,MAA+B,KAAc,WACnD,MAAM,WAAW,QAAQ,MAAe,KAAK,EAAE,OAAO,CAAC;AAC3D,QAAM,gBAAgB,KAAK,cAAc,iBAAiB;AAE1D,SAAO;AAAA,IACL,YAA6B;AAC3B,aAAO,OAAO,IAAI,CAAC,UAAU,eAAe,MAAM,MAAM,MAAM,OAAO,CAAC;AAAA,IACxE;AAAA,IAEA,MAAM,SAAS,MAAc,MAA6D;AACxF,UAAI,CAAC,QAAQ,IAAI,IAAyB,GAAG;AAC3C,eAAO;AAAA,UACL,SAAS,SAAS,IAAI;AAAA,UACtB,SAAS;AAAA,QACX;AAAA,MACF;AAEA,UAAI,SAAS,gBAAgB;AAC3B,YAAI;AACF,gBAAM,UAAU,MAAM,uBAAuB,aAAa,MAAM,aAAa;AAC7E,iBAAO,EAAE,SAAS,SAAS,MAAM;AAAA,QACnC,SAAS,OAAO;AACd,iBAAO;AAAA,YACL,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,YAC9D,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF;AAEA,YAAM,UAAU,QAAQ,IAAI,IAAyB;AACrD,YAAM,SAAS,KAAK,QAAQ;AAC5B,UAAI,OAAO,WAAW,YAAY,CAAC,SAAS,SAAS,MAAyB,GAAG;AAC/E,eAAO;AAAA,UACL,SAAS,WAAW,OAAO,MAAM,CAAC,4BAA4B,IAAI;AAAA,UAClE,SAAS;AAAA,QACX;AAAA,MACF;AAEA,UAAI;AACF,cAAM,SAAS,MAAM,cAAc,MAAM,SAAS,IAAI,gBAAgB,EAAE,MAAM;AAC9E,cAAM,SACJ,CAAC,CAAC,UACF,OAAO,WAAW,YAClB,CAAC,MAAM,QAAQ,MAAM,KACpB,OAAmC,IAAI,MAAM;AAChD,eAAO,EAAE,SAAS,QAAQ,SAAS,OAAO;AAAA,MAC5C,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,UAC9D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,sBACd,aACA,OAAiC,CAAC,GACvB;AACX,SAAO,IAAI,UAAU;AAAA,IACnB,MAAM,wBAAwB,aAAa,IAAI;AAAA,IAC/C,YAAY,EAAE,MAAM,yBAAyB,SAAS,YAAY,QAAQ;AAAA,IAC1E,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,OAAO;AAAA,QACP,aACE;AAAA,QACF,WAAW,CAAC,EAAE,MAAM,WAAW,aAAa,6BAA6B,UAAU,KAAK,CAAC;AAAA,QACzF,UACE;AAAA,MACJ;AAAA,IACF;AAAA,EACF,CAAC;AACH;",
|
|
6
|
+
"names": ["resolve"]
|
|
7
|
+
}
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const KANBAN_READ_ACTIONS: readonly ['list_boards', 'get_board', 'export_markdown', 'export_task_graph', 'search_tasks', 'ready_tasks', 'snapshot', 'get_task', 'get_chain', 'events', 'queue_health'];
|
|
2
|
+
export declare const KANBAN_MANAGE_ACTIONS: readonly ['create_board', 'duplicate_board', 'update_board', 'adopt_managed_lifecycle', 'generate_board', 'sync_task_graph', 'create_from_graph', 'import_session_tasks', 'add_column', 'update_column', 'add_task', 'split_task', 'copy_task', 'update_task', 'transition_task', 'repair_managed_projection', 'move_task', 'set_chain', 'claim_task', 'release_task', 'assign_task', 'mark_assignment', 'heartbeat_assignment', 'recover_stale', 'add_dependency', 'add_goal_metric', 'update_goal_metric', 'add_check', 'update_check', 'add_note', 'add_link', 'verify_completion', 'split_atomic', 'assess_atomicity', 'propose_decomposition'];
|
|
3
|
+
/** Operations that remove or absorb durable task/board state. */
|
|
4
|
+
export declare const KANBAN_DESTRUCTIVE_ACTIONS: readonly ['delete_board', 'delete_column', 'delete_task', 'merge_tasks', 'transfer_task'];
|
|
5
|
+
export type KanbanReadAction = (typeof KANBAN_READ_ACTIONS)[number];
|
|
6
|
+
export type KanbanManageAction = (typeof KANBAN_MANAGE_ACTIONS)[number];
|
|
7
|
+
export type KanbanDestructiveAction = (typeof KANBAN_DESTRUCTIVE_ACTIONS)[number];
|
|
8
|
+
export type KanbanMcpAction = KanbanReadAction | KanbanManageAction | KanbanDestructiveAction;
|
|
9
|
+
export type KanbanMcpToolName = 'kanban_read' | 'kanban_manage' | 'kanban_destructive' | 'kanban_watch';
|
|
10
|
+
export interface KanbanMcpPolicyOptions {
|
|
11
|
+
writable?: boolean;
|
|
12
|
+
destructive?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface KanbanMcpToolPolicy {
|
|
15
|
+
name: KanbanMcpToolName;
|
|
16
|
+
actions?: readonly KanbanMcpAction[];
|
|
17
|
+
}
|
|
18
|
+
export declare function selectKanbanMcpTools(opts?: KanbanMcpPolicyOptions): KanbanMcpToolPolicy[];
|
|
19
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,YAC9B,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,UAAU,EACV,UAAU,EACV,WAAW,EACX,QAAQ,EACR,cAAc,CACN,CAAC;AAEX,eAAO,MAAM,qBAAqB,YAChC,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,yBAAyB,EACzB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,YAAY,EACZ,eAAe,EACf,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,2BAA2B,EAC3B,WAAW,EACX,WAAW,EACX,YAAY,EACZ,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,WAAW,EACX,cAAc,EACd,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,uBAAuB,CACf,CAAC;AAEX,iEAAiE;AACjE,eAAO,MAAM,0BAA0B,YACrC,cAAc,EACd,eAAe,EACf,aAAa,EACb,aAAa,EACb,eAAe,CACP,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AACpE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AACxE,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAC;AAClF,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG,kBAAkB,GAAG,uBAAuB,CAAC;AAC9F,MAAM,MAAM,iBAAiB,GACzB,aAAa,GACb,eAAe,GACf,oBAAoB,GACpB,cAAc,CAAC;AAEnB,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;CACtC;AAED,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,sBAA2B,GAAG,mBAAmB,EAAE,CAY7F"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AA4BA,eAAO,MAAM,WAAW;UAdW,MAAM;aAAW,MAAM;CAcf,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wrongstack/kanban-mcp",
|
|
3
|
+
"version": "0.297.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "WrongStack Kanban as a project-scoped MCP server with read-only, writable, destructive, and live-watch capability tiers.",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/WrongStack/WrongStack.git",
|
|
9
|
+
"directory": "packages/kanban-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-kanban-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
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@wrongstack/mcp": "0.297.0",
|
|
35
|
+
"@wrongstack/core": "0.297.0",
|
|
36
|
+
"@wrongstack/kanban": "0.297.0",
|
|
37
|
+
"@wrongstack/tools": "0.297.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^26.1.1",
|
|
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/kanban-mcp tests from the workspace root: pnpm exec vitest run packages/kanban-mcp/tests\"",
|
|
51
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\""
|
|
52
|
+
}
|
|
53
|
+
}
|