backlog-mcp 0.20.0 → 0.23.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.
Files changed (174) hide show
  1. package/README.md +15 -0
  2. package/dist/cli/bridge.d.mts +1 -0
  3. package/dist/cli/bridge.mjs +30 -0
  4. package/dist/cli/bridge.mjs.map +1 -0
  5. package/dist/cli/index.d.mts +1 -0
  6. package/dist/{cli.js → cli/index.mjs} +13 -22
  7. package/dist/cli/index.mjs.map +1 -0
  8. package/dist/cli/server-manager.d.mts +5 -0
  9. package/dist/cli/server-manager.mjs +93 -0
  10. package/dist/cli/server-manager.mjs.map +1 -0
  11. package/dist/index.d.mts +4 -0
  12. package/dist/index.mjs +5 -0
  13. package/dist/middleware/auth.d.mts +7 -0
  14. package/dist/middleware/auth.mjs +11 -0
  15. package/dist/middleware/auth.mjs.map +1 -0
  16. package/dist/resources/index-resources.d.mts +7 -0
  17. package/dist/resources/index-resources.mjs +16 -0
  18. package/dist/resources/index-resources.mjs.map +1 -0
  19. package/dist/resources/index.d.mts +3 -0
  20. package/dist/resources/index.mjs +3 -0
  21. package/dist/resources/operations.d.mts +7 -0
  22. package/dist/resources/operations.mjs +26 -0
  23. package/dist/resources/operations.mjs.map +1 -0
  24. package/dist/resources/resource-file.d.mts +7 -0
  25. package/dist/resources/resource-file.mjs +20 -0
  26. package/dist/resources/resource-file.mjs.map +1 -0
  27. package/dist/resources/resource-reader.d.mts +10 -0
  28. package/dist/resources/resource-reader.mjs +34 -0
  29. package/dist/resources/resource-reader.mjs.map +1 -0
  30. package/dist/resources/task-attached.d.mts +7 -0
  31. package/dist/resources/task-attached.mjs +17 -0
  32. package/dist/resources/task-attached.mjs.map +1 -0
  33. package/dist/resources/task-by-id.d.mts +7 -0
  34. package/dist/resources/task-by-id.mjs +23 -0
  35. package/dist/resources/task-by-id.mjs.map +1 -0
  36. package/dist/resources/tasks.d.mts +7 -0
  37. package/dist/resources/tasks.mjs +20 -0
  38. package/dist/resources/tasks.mjs.map +1 -0
  39. package/dist/resources/types.d.mts +33 -0
  40. package/dist/resources/types.mjs +1 -0
  41. package/dist/resources/uri.d.mts +11 -0
  42. package/dist/resources/uri.mjs +22 -0
  43. package/dist/resources/uri.mjs.map +1 -0
  44. package/dist/resources/write.d.mts +11 -0
  45. package/dist/resources/write.mjs +63 -0
  46. package/dist/resources/write.mjs.map +1 -0
  47. package/dist/server/fastify-server.d.mts +5 -0
  48. package/dist/server/fastify-server.mjs +50 -0
  49. package/dist/server/fastify-server.mjs.map +1 -0
  50. package/dist/server/mcp-handler.d.mts +7 -0
  51. package/dist/server/mcp-handler.mjs +32 -0
  52. package/dist/server/mcp-handler.mjs.map +1 -0
  53. package/dist/server/viewer-routes.d.mts +7 -0
  54. package/dist/server/viewer-routes.mjs +120 -0
  55. package/dist/server/viewer-routes.mjs.map +1 -0
  56. package/dist/storage/backlog.d.mts +37 -0
  57. package/dist/storage/backlog.mjs +127 -0
  58. package/dist/storage/backlog.mjs.map +1 -0
  59. package/dist/storage/schema.d.mts +38 -0
  60. package/dist/storage/schema.mjs +44 -0
  61. package/dist/storage/schema.mjs.map +1 -0
  62. package/dist/tools/backlog-create.d.mts +7 -0
  63. package/dist/tools/backlog-create.mjs +38 -0
  64. package/dist/tools/backlog-create.mjs.map +1 -0
  65. package/dist/tools/backlog-delete.d.mts +7 -0
  66. package/dist/tools/backlog-delete.mjs +20 -0
  67. package/dist/tools/backlog-delete.mjs.map +1 -0
  68. package/dist/tools/backlog-get.d.mts +7 -0
  69. package/dist/tools/backlog-get.mjs +27 -0
  70. package/dist/tools/backlog-get.mjs.map +1 -0
  71. package/dist/tools/backlog-list.d.mts +7 -0
  72. package/dist/tools/backlog-list.mjs +39 -0
  73. package/dist/tools/backlog-list.mjs.map +1 -0
  74. package/dist/tools/backlog-update.d.mts +7 -0
  75. package/dist/tools/backlog-update.mjs +42 -0
  76. package/dist/tools/backlog-update.mjs.map +1 -0
  77. package/dist/tools/index.d.mts +7 -0
  78. package/dist/tools/index.mjs +18 -0
  79. package/dist/tools/index.mjs.map +1 -0
  80. package/dist/utils/paths.d.mts +72 -0
  81. package/dist/utils/paths.mjs +117 -0
  82. package/dist/utils/paths.mjs.map +1 -0
  83. package/dist/utils/uri-resolver.d.mts +8 -0
  84. package/dist/utils/uri-resolver.mjs +45 -0
  85. package/dist/utils/uri-resolver.mjs.map +1 -0
  86. package/dist/viewer/chevron-CBYYYF2L.svg +3 -0
  87. package/dist/viewer/copy-3LO2VTYZ.svg +4 -0
  88. package/dist/viewer/epic-OFTO5ZU3.svg +4 -0
  89. package/dist/viewer/external-link-5YHGZUVF.svg +3 -0
  90. package/dist/viewer/file-IS5HWDMR.svg +3 -0
  91. package/dist/viewer/index.html +46 -0
  92. package/dist/viewer/lightning-bolt-KZ4Y6ALR.svg +3 -0
  93. package/dist/viewer/logo.svg +14 -0
  94. package/dist/viewer/main.css +1728 -0
  95. package/dist/viewer/main.js +46 -12
  96. package/dist/viewer/pin-CTBSQJY3.svg +3 -0
  97. package/dist/viewer/task-QBLM24U4.svg +3 -0
  98. package/package.json +18 -15
  99. package/viewer/components/md-block.ts +305 -0
  100. package/viewer/components/resource-viewer.ts +227 -0
  101. package/viewer/components/svg-icon.ts +72 -0
  102. package/viewer/components/task-badge.ts +26 -0
  103. package/viewer/components/task-detail.ts +125 -0
  104. package/viewer/components/task-filter-bar.ts +70 -0
  105. package/viewer/components/task-item.ts +81 -0
  106. package/viewer/components/task-list.ts +143 -0
  107. package/viewer/icons/chevron.svg +3 -0
  108. package/viewer/icons/copy.svg +4 -0
  109. package/viewer/icons/epic.svg +4 -0
  110. package/viewer/icons/external-link.svg +3 -0
  111. package/viewer/icons/file.svg +3 -0
  112. package/viewer/icons/gradient-icons-showcase.html +280 -0
  113. package/viewer/icons/gradient-icons.ts +68 -0
  114. package/viewer/icons/hexagon.svg +3 -0
  115. package/viewer/icons/index.ts +12 -0
  116. package/viewer/icons/infinity.svg +3 -0
  117. package/viewer/icons/lightning-bolt-animated.svg +3 -0
  118. package/viewer/icons/lightning-bolt.svg +3 -0
  119. package/viewer/icons/pin.svg +3 -0
  120. package/viewer/icons/portal.svg +5 -0
  121. package/viewer/icons/prism.svg +3 -0
  122. package/viewer/icons/ring.svg +3 -0
  123. package/viewer/icons/sparkle.svg +3 -0
  124. package/viewer/icons/star.svg +3 -0
  125. package/viewer/icons/task.svg +3 -0
  126. package/viewer/icons/wave.svg +3 -0
  127. package/viewer/index.html +1 -2
  128. package/viewer/main.ts +85 -0
  129. package/viewer/styles.css +11 -1
  130. package/viewer/svg.d.ts +4 -0
  131. package/viewer/tsconfig.json +16 -0
  132. package/viewer/utils/api.ts +38 -0
  133. package/viewer/utils/layout.ts +17 -0
  134. package/viewer/utils/resize.ts +76 -0
  135. package/viewer/utils/split-pane.ts +208 -0
  136. package/viewer/utils/url-state.ts +55 -0
  137. package/dist/backlog.d.ts +0 -33
  138. package/dist/backlog.js +0 -144
  139. package/dist/backlog.js.map +0 -1
  140. package/dist/cli/bridge.d.ts +0 -2
  141. package/dist/cli/bridge.js +0 -170
  142. package/dist/cli/bridge.js.map +0 -1
  143. package/dist/cli.d.ts +0 -2
  144. package/dist/cli.js.map +0 -1
  145. package/dist/http-server.d.ts +0 -2
  146. package/dist/http-server.js +0 -380
  147. package/dist/http-server.js.map +0 -1
  148. package/dist/index.d.ts +0 -3
  149. package/dist/index.js +0 -7
  150. package/dist/index.js.map +0 -1
  151. package/dist/resource-reader.d.ts +0 -6
  152. package/dist/resource-reader.js +0 -33
  153. package/dist/resource-reader.js.map +0 -1
  154. package/dist/resources/index.d.ts +0 -3
  155. package/dist/resources/index.js +0 -3
  156. package/dist/resources/index.js.map +0 -1
  157. package/dist/resources/operations.d.ts +0 -2
  158. package/dist/resources/operations.js +0 -35
  159. package/dist/resources/operations.js.map +0 -1
  160. package/dist/resources/types.d.ts +0 -29
  161. package/dist/resources/types.js +0 -3
  162. package/dist/resources/types.js.map +0 -1
  163. package/dist/resources/uri.d.ts +0 -7
  164. package/dist/resources/uri.js +0 -26
  165. package/dist/resources/uri.js.map +0 -1
  166. package/dist/resources/write.d.ts +0 -6
  167. package/dist/resources/write.js +0 -76
  168. package/dist/resources/write.js.map +0 -1
  169. package/dist/schema.d.ts +0 -34
  170. package/dist/schema.js +0 -46
  171. package/dist/schema.js.map +0 -1
  172. package/dist/uri-resolver.d.ts +0 -4
  173. package/dist/uri-resolver.js +0 -70
  174. package/dist/uri-resolver.js.map +0 -1
package/README.md CHANGED
@@ -92,6 +92,21 @@ pnpm install && pnpm build
92
92
  pnpm start
93
93
  ```
94
94
 
95
+ ## Configuration
96
+
97
+ Environment variables (create `.env` file for local development):
98
+
99
+ ```bash
100
+ # Copy example config
101
+ cp .env.example .env
102
+
103
+ # Edit with your values
104
+ BACKLOG_DATA_DIR=/Users/yourname/.backlog # Where to store tasks
105
+ BACKLOG_VIEWER_PORT=3030 # HTTP server port
106
+ ```
107
+
108
+ **For agents/LLMs:** Read the actual `.env` file to understand the configured values, not the defaults in code.
109
+
95
110
  ## Storage
96
111
 
97
112
  - Default: `data/tasks/` and `data/archive/` (local to project)
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import { paths } from "../utils/paths.mjs";
3
+ import { ensureServer } from "./server-manager.mjs";
4
+ import { existsSync } from "node:fs";
5
+ import { spawn } from "node:child_process";
6
+
7
+ //#region src/cli/bridge.ts
8
+ async function runBridge(port) {
9
+ await ensureServer(port);
10
+ const serverUrl = `http://localhost:${port}/mcp`;
11
+ const mcpRemotePath = paths.getBinPath("mcp-remote");
12
+ if (!existsSync(mcpRemotePath)) {
13
+ console.error("mcp-remote not found. Please run: pnpm install");
14
+ process.exit(1);
15
+ }
16
+ spawn(mcpRemotePath, [
17
+ serverUrl,
18
+ "--allow-http",
19
+ "--transport",
20
+ "http-only"
21
+ ], { stdio: "inherit" }).on("exit", (code) => process.exit(code || 0));
22
+ }
23
+ runBridge(parseInt(process.env.BACKLOG_VIEWER_PORT || "3030")).catch((error) => {
24
+ console.error("Bridge error:", error);
25
+ process.exit(1);
26
+ });
27
+
28
+ //#endregion
29
+ export { };
30
+ //# sourceMappingURL=bridge.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.mjs","names":[],"sources":["../../src/cli/bridge.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { ensureServer } from './server-manager.js';\nimport { paths } from '@/utils/paths.js';\n\nasync function runBridge(port: number): Promise<void> {\n await ensureServer(port);\n \n const serverUrl = `http://localhost:${port}/mcp`;\n const mcpRemotePath = paths.getBinPath('mcp-remote');\n \n if (!existsSync(mcpRemotePath)) {\n console.error('mcp-remote not found. Please run: pnpm install');\n process.exit(1);\n }\n \n const bridge = spawn(mcpRemotePath, [serverUrl, '--allow-http', '--transport', 'http-only'], {\n stdio: 'inherit'\n });\n \n bridge.on('exit', (code) => process.exit(code || 0));\n}\n\nconst port = parseInt(process.env.BACKLOG_VIEWER_PORT || '3030');\nrunBridge(port).catch((error) => {\n console.error('Bridge error:', error);\n process.exit(1);\n});\n"],"mappings":";;;;;;;AAOA,eAAe,UAAU,MAA6B;AACpD,OAAM,aAAa,KAAK;CAExB,MAAM,YAAY,oBAAoB,KAAK;CAC3C,MAAM,gBAAgB,MAAM,WAAW,aAAa;AAEpD,KAAI,CAAC,WAAW,cAAc,EAAE;AAC9B,UAAQ,MAAM,iDAAiD;AAC/D,UAAQ,KAAK,EAAE;;AAOjB,CAJe,MAAM,eAAe;EAAC;EAAW;EAAgB;EAAe;EAAY,EAAE,EAC3F,OAAO,WACR,CAAC,CAEK,GAAG,SAAS,SAAS,QAAQ,KAAK,QAAQ,EAAE,CAAC;;AAItD,UADa,SAAS,QAAQ,IAAI,uBAAuB,OAAO,CACjD,CAAC,OAAO,UAAU;AAC/B,SAAQ,MAAM,iBAAiB,MAAM;AACrC,SAAQ,KAAK,EAAE;EACf"}
@@ -0,0 +1 @@
1
+ export { };
@@ -1,18 +1,11 @@
1
1
  #!/usr/bin/env node
2
- try {
3
- await import('dotenv/config');
4
- }
5
- catch { }
6
- const args = process.argv.slice(2);
7
- const command = args[0];
8
- if (command === 'serve') {
9
- // HTTP server mode
10
- const { startHttpServer } = await import('./http-server.js');
11
- const port = parseInt(process.env.BACKLOG_VIEWER_PORT || '3030');
12
- await startHttpServer(port);
13
- }
14
- else if (command === '--help' || command === '-h') {
15
- console.log(`
2
+ //#region src/cli/index.ts
3
+ const command = process.argv.slice(2)[0];
4
+ if (command === "serve") {
5
+ const { startHttpServer } = await import("../server/fastify-server.mjs");
6
+ await startHttpServer(parseInt(process.env.BACKLOG_VIEWER_PORT || "3030"));
7
+ } else if (command === "--help" || command === "-h") {
8
+ console.log(`
16
9
  backlog-mcp - Task management MCP server
17
10
 
18
11
  Usage:
@@ -29,11 +22,9 @@ How it works:
29
22
  - HTTP server persists across sessions (shared by multiple clients)
30
23
  - Automatic version upgrades on server restart
31
24
  `);
32
- process.exit(0);
33
- }
34
- else {
35
- // Default: bridge mode (auto-spawn HTTP server)
36
- await import('./cli/bridge.js');
37
- }
38
- export {};
39
- //# sourceMappingURL=cli.js.map
25
+ process.exit(0);
26
+ } else await import("./bridge.mjs");
27
+
28
+ //#endregion
29
+ export { };
30
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nif (command === 'serve') {\n // HTTP server mode\n const { startHttpServer } = await import('../server/fastify-server.js');\n const port = parseInt(process.env.BACKLOG_VIEWER_PORT || '3030');\n await startHttpServer(port);\n} else if (command === '--help' || command === '-h') {\n console.log(`\nbacklog-mcp - Task management MCP server\n\nUsage:\n backlog-mcp Run as stdio MCP server (auto-bridges to HTTP server)\n backlog-mcp serve Run as HTTP MCP server with viewer\n backlog-mcp --help Show this help\n\nEnvironment variables:\n BACKLOG_DATA_DIR Data directory path (default: ./data)\n BACKLOG_VIEWER_PORT HTTP server port (default: 3030)\n\nHow it works:\n - Default mode auto-spawns HTTP server and bridges stdio to it\n - HTTP server persists across sessions (shared by multiple clients)\n - Automatic version upgrades on server restart\n `);\n process.exit(0);\n} else {\n // Default: bridge mode (auto-spawn HTTP server)\n await import('./bridge.js');\n}\n"],"mappings":";;AAGA,MAAM,UADO,QAAQ,KAAK,MAAM,EAAE,CACb;AAErB,IAAI,YAAY,SAAS;CAEvB,MAAM,EAAE,oBAAoB,MAAM,OAAO;AAEzC,OAAM,gBADO,SAAS,QAAQ,IAAI,uBAAuB,OAAO,CACrC;WAClB,YAAY,YAAY,YAAY,MAAM;AACnD,SAAQ,IAAI;;;;;;;;;;;;;;;;IAgBV;AACF,SAAQ,KAAK,EAAE;MAGf,OAAM,OAAO"}
@@ -0,0 +1,5 @@
1
+ //#region src/cli/server-manager.d.ts
2
+ declare function ensureServer(port: number): Promise<void>;
3
+ //#endregion
4
+ export { ensureServer };
5
+ //# sourceMappingURL=server-manager.d.mts.map
@@ -0,0 +1,93 @@
1
+ import { paths } from "../utils/paths.mjs";
2
+ import { join } from "node:path";
3
+ import { spawn } from "node:child_process";
4
+ import { request } from "node:http";
5
+
6
+ //#region src/cli/server-manager.ts
7
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8
+ async function isServerRunning(port) {
9
+ return new Promise((resolve) => {
10
+ const req = request({
11
+ host: "localhost",
12
+ port,
13
+ path: "/version",
14
+ method: "GET"
15
+ }, (res) => {
16
+ resolve(res.statusCode === 200);
17
+ });
18
+ req.on("error", () => resolve(false));
19
+ req.end();
20
+ });
21
+ }
22
+ async function getServerVersion(port) {
23
+ return new Promise((resolve) => {
24
+ const req = request({
25
+ host: "localhost",
26
+ port,
27
+ path: "/version",
28
+ method: "GET"
29
+ }, (res) => {
30
+ if (res.statusCode !== 200) {
31
+ resolve(null);
32
+ return;
33
+ }
34
+ let data = "";
35
+ res.on("data", (chunk) => data += chunk);
36
+ res.on("end", () => resolve(data.trim()));
37
+ });
38
+ req.on("error", () => resolve(null));
39
+ req.end();
40
+ });
41
+ }
42
+ async function spawnServer(port) {
43
+ const serverPath = join(paths.distRoot, "server", "fastify-server.js");
44
+ spawn(process.execPath, [serverPath], {
45
+ detached: true,
46
+ stdio: "ignore",
47
+ env: {
48
+ ...process.env,
49
+ BACKLOG_VIEWER_PORT: String(port)
50
+ }
51
+ }).unref();
52
+ }
53
+ async function shutdownServer(port) {
54
+ return new Promise((resolve) => {
55
+ const req = request({
56
+ host: "localhost",
57
+ port,
58
+ path: "/shutdown",
59
+ method: "POST"
60
+ }, () => {
61
+ resolve();
62
+ });
63
+ req.on("error", () => resolve());
64
+ req.end();
65
+ });
66
+ }
67
+ async function waitForServer(port, timeout) {
68
+ const start = Date.now();
69
+ let delay = 100;
70
+ while (Date.now() - start < timeout) {
71
+ if (await isServerRunning(port)) return;
72
+ await sleep(delay);
73
+ delay = Math.min(delay * 1.5, 1e3);
74
+ }
75
+ throw new Error(`Server failed to start within ${timeout}ms`);
76
+ }
77
+ async function ensureServer(port) {
78
+ if (!await isServerRunning(port)) {
79
+ await spawnServer(port);
80
+ await waitForServer(port, 1e4);
81
+ return;
82
+ }
83
+ if (await getServerVersion(port) !== paths.getVersion()) {
84
+ await shutdownServer(port);
85
+ await sleep(1e3);
86
+ await spawnServer(port);
87
+ await waitForServer(port, 1e4);
88
+ }
89
+ }
90
+
91
+ //#endregion
92
+ export { ensureServer };
93
+ //# sourceMappingURL=server-manager.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-manager.mjs","names":[],"sources":["../../src/cli/server-manager.ts"],"sourcesContent":["import { request } from 'node:http';\nimport { spawn } from 'node:child_process';\nimport { join } from 'node:path';\nimport { paths } from '@/utils/paths.js';\n\nconst sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));\n\nasync function isServerRunning(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const req = request({ host: 'localhost', port, path: '/version', method: 'GET' }, (res) => {\n resolve(res.statusCode === 200);\n });\n req.on('error', () => resolve(false));\n req.end();\n });\n}\n\nasync function getServerVersion(port: number): Promise<string | null> {\n return new Promise((resolve) => {\n const req = request({ host: 'localhost', port, path: '/version', method: 'GET' }, (res) => {\n if (res.statusCode !== 200) {\n resolve(null);\n return;\n }\n let data = '';\n res.on('data', chunk => data += chunk);\n res.on('end', () => resolve(data.trim()));\n });\n req.on('error', () => resolve(null));\n req.end();\n });\n}\n\nasync function spawnServer(port: number): Promise<void> {\n const serverPath = join(paths.distRoot, 'server', 'fastify-server.js');\n const child = spawn(process.execPath, [serverPath], {\n detached: true,\n stdio: 'ignore',\n env: { ...process.env, BACKLOG_VIEWER_PORT: String(port) }\n });\n child.unref();\n}\n\nasync function shutdownServer(port: number): Promise<void> {\n return new Promise((resolve) => {\n const req = request({ host: 'localhost', port, path: '/shutdown', method: 'POST' }, () => {\n resolve();\n });\n req.on('error', () => resolve());\n req.end();\n });\n}\n\nasync function waitForServer(port: number, timeout: number): Promise<void> {\n const start = Date.now();\n let delay = 100;\n \n while (Date.now() - start < timeout) {\n if (await isServerRunning(port)) return;\n await sleep(delay);\n delay = Math.min(delay * 1.5, 1000);\n }\n \n throw new Error(`Server failed to start within ${timeout}ms`);\n}\n\nexport async function ensureServer(port: number): Promise<void> {\n const running = await isServerRunning(port);\n \n if (!running) {\n await spawnServer(port);\n await waitForServer(port, 10000);\n return;\n }\n \n const serverVersion = await getServerVersion(port);\n if (serverVersion !== paths.getVersion()) {\n await shutdownServer(port);\n await sleep(1000);\n await spawnServer(port);\n await waitForServer(port, 10000);\n }\n}\n"],"mappings":";;;;;;AAKA,MAAM,SAAS,OAAe,IAAI,SAAQ,YAAW,WAAW,SAAS,GAAG,CAAC;AAE7E,eAAe,gBAAgB,MAAgC;AAC7D,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,MAAM,QAAQ;GAAE,MAAM;GAAa;GAAM,MAAM;GAAY,QAAQ;GAAO,GAAG,QAAQ;AACzF,WAAQ,IAAI,eAAe,IAAI;IAC/B;AACF,MAAI,GAAG,eAAe,QAAQ,MAAM,CAAC;AACrC,MAAI,KAAK;GACT;;AAGJ,eAAe,iBAAiB,MAAsC;AACpE,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,MAAM,QAAQ;GAAE,MAAM;GAAa;GAAM,MAAM;GAAY,QAAQ;GAAO,GAAG,QAAQ;AACzF,OAAI,IAAI,eAAe,KAAK;AAC1B,YAAQ,KAAK;AACb;;GAEF,IAAI,OAAO;AACX,OAAI,GAAG,SAAQ,UAAS,QAAQ,MAAM;AACtC,OAAI,GAAG,aAAa,QAAQ,KAAK,MAAM,CAAC,CAAC;IACzC;AACF,MAAI,GAAG,eAAe,QAAQ,KAAK,CAAC;AACpC,MAAI,KAAK;GACT;;AAGJ,eAAe,YAAY,MAA6B;CACtD,MAAM,aAAa,KAAK,MAAM,UAAU,UAAU,oBAAoB;AAMtE,CALc,MAAM,QAAQ,UAAU,CAAC,WAAW,EAAE;EAClD,UAAU;EACV,OAAO;EACP,KAAK;GAAE,GAAG,QAAQ;GAAK,qBAAqB,OAAO,KAAK;GAAE;EAC3D,CAAC,CACI,OAAO;;AAGf,eAAe,eAAe,MAA6B;AACzD,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,MAAM,QAAQ;GAAE,MAAM;GAAa;GAAM,MAAM;GAAa,QAAQ;GAAQ,QAAQ;AACxF,YAAS;IACT;AACF,MAAI,GAAG,eAAe,SAAS,CAAC;AAChC,MAAI,KAAK;GACT;;AAGJ,eAAe,cAAc,MAAc,SAAgC;CACzE,MAAM,QAAQ,KAAK,KAAK;CACxB,IAAI,QAAQ;AAEZ,QAAO,KAAK,KAAK,GAAG,QAAQ,SAAS;AACnC,MAAI,MAAM,gBAAgB,KAAK,CAAE;AACjC,QAAM,MAAM,MAAM;AAClB,UAAQ,KAAK,IAAI,QAAQ,KAAK,IAAK;;AAGrC,OAAM,IAAI,MAAM,iCAAiC,QAAQ,IAAI;;AAG/D,eAAsB,aAAa,MAA6B;AAG9D,KAAI,CAFY,MAAM,gBAAgB,KAAK,EAE7B;AACZ,QAAM,YAAY,KAAK;AACvB,QAAM,cAAc,MAAM,IAAM;AAChC;;AAIF,KADsB,MAAM,iBAAiB,KAAK,KAC5B,MAAM,YAAY,EAAE;AACxC,QAAM,eAAe,KAAK;AAC1B,QAAM,MAAM,IAAK;AACjB,QAAM,YAAY,KAAK;AACvB,QAAM,cAAc,MAAM,IAAM"}
@@ -0,0 +1,4 @@
1
+ import { CreateTaskInput, STATUSES, Status, Task, createTask, formatTaskId, isValidTaskId, nextTaskId, parseTaskId } from "./storage/schema.mjs";
2
+ import { storage } from "./storage/backlog.mjs";
3
+ import { startHttpServer } from "./server/fastify-server.mjs";
4
+ export { type CreateTaskInput, STATUSES, type Status, type Task, createTask, formatTaskId, isValidTaskId, nextTaskId, parseTaskId, startHttpServer, storage };
package/dist/index.mjs ADDED
@@ -0,0 +1,5 @@
1
+ import { STATUSES, createTask, formatTaskId, isValidTaskId, nextTaskId, parseTaskId } from "./storage/schema.mjs";
2
+ import { storage } from "./storage/backlog.mjs";
3
+ import { startHttpServer } from "./server/fastify-server.mjs";
4
+
5
+ export { STATUSES, createTask, formatTaskId, isValidTaskId, nextTaskId, parseTaskId, startHttpServer, storage };
@@ -0,0 +1,7 @@
1
+ import { FastifyReply, FastifyRequest } from "fastify";
2
+
3
+ //#region src/middleware/auth.d.ts
4
+ declare function authMiddleware(request: FastifyRequest, reply: FastifyReply): Promise<undefined>;
5
+ //#endregion
6
+ export { authMiddleware };
7
+ //# sourceMappingURL=auth.d.mts.map
@@ -0,0 +1,11 @@
1
+ //#region src/middleware/auth.ts
2
+ async function authMiddleware(request, reply) {
3
+ if (request.url === "/health" || request.url === "/version") return;
4
+ if (request.url.startsWith("/mcp") && process.env.API_KEY) {
5
+ if (request.headers.authorization !== `Bearer ${process.env.API_KEY}`) return reply.code(401).send({ error: "Unauthorized" });
6
+ }
7
+ }
8
+
9
+ //#endregion
10
+ export { authMiddleware };
11
+ //# sourceMappingURL=auth.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.mjs","names":[],"sources":["../../src/middleware/auth.ts"],"sourcesContent":["import type { FastifyRequest, FastifyReply } from 'fastify';\n\nexport async function authMiddleware(\n request: FastifyRequest,\n reply: FastifyReply\n) {\n // Skip auth for health check and version\n if (request.url === '/health' || request.url === '/version') {\n return;\n }\n\n // API key auth for MCP endpoint (optional, for cloud deployment)\n if (request.url.startsWith('/mcp') && process.env.API_KEY) {\n const apiKey = request.headers.authorization;\n if (apiKey !== `Bearer ${process.env.API_KEY}`) {\n return reply.code(401).send({ error: 'Unauthorized' });\n }\n }\n}\n"],"mappings":";AAEA,eAAsB,eACpB,SACA,OACA;AAEA,KAAI,QAAQ,QAAQ,aAAa,QAAQ,QAAQ,WAC/C;AAIF,KAAI,QAAQ,IAAI,WAAW,OAAO,IAAI,QAAQ,IAAI,SAEhD;MADe,QAAQ,QAAQ,kBAChB,UAAU,QAAQ,IAAI,UACnC,QAAO,MAAM,KAAK,IAAI,CAAC,KAAK,EAAE,OAAO,gBAAgB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+
3
+ //#region src/resources/index-resources.d.ts
4
+ declare function registerResources(server: McpServer): void;
5
+ //#endregion
6
+ export { registerResources };
7
+ //# sourceMappingURL=index-resources.d.mts.map
@@ -0,0 +1,16 @@
1
+ import { registerTasksResource } from "./tasks.mjs";
2
+ import { registerTaskByIdResource } from "./task-by-id.mjs";
3
+ import { registerTaskAttachedResource } from "./task-attached.mjs";
4
+ import { registerResourceFileResource } from "./resource-file.mjs";
5
+
6
+ //#region src/resources/index-resources.ts
7
+ function registerResources(server) {
8
+ registerTasksResource(server);
9
+ registerTaskByIdResource(server);
10
+ registerTaskAttachedResource(server);
11
+ registerResourceFileResource(server);
12
+ }
13
+
14
+ //#endregion
15
+ export { registerResources };
16
+ //# sourceMappingURL=index-resources.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-resources.mjs","names":[],"sources":["../../src/resources/index-resources.ts"],"sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { registerTasksResource } from './tasks.js';\nimport { registerTaskByIdResource } from './task-by-id.js';\nimport { registerTaskAttachedResource } from './task-attached.js';\nimport { registerResourceFileResource } from './resource-file.js';\n\nexport function registerResources(server: McpServer) {\n registerTasksResource(server);\n registerTaskByIdResource(server);\n registerTaskAttachedResource(server);\n registerResourceFileResource(server);\n}\n"],"mappings":";;;;;;AAMA,SAAgB,kBAAkB,QAAmB;AACnD,uBAAsB,OAAO;AAC7B,0BAAyB,OAAO;AAChC,8BAA6B,OAAO;AACpC,8BAA6B,OAAO"}
@@ -0,0 +1,3 @@
1
+ import { Operation, WriteResourceResult } from "./types.mjs";
2
+ import { writeResource } from "./write.mjs";
3
+ export { type Operation, type WriteResourceResult, writeResource };
@@ -0,0 +1,3 @@
1
+ import { writeResource } from "./write.mjs";
2
+
3
+ export { writeResource };
@@ -0,0 +1,7 @@
1
+ import { Operation } from "./types.mjs";
2
+
3
+ //#region src/resources/operations.d.ts
4
+ declare function applyOperation(content: string, operation: Operation): string;
5
+ //#endregion
6
+ export { applyOperation };
7
+ //# sourceMappingURL=operations.d.mts.map
@@ -0,0 +1,26 @@
1
+ //#region src/resources/operations.ts
2
+ function applyOperation(content, operation) {
3
+ switch (operation.type) {
4
+ case "str_replace": {
5
+ const { old_str, new_str } = operation;
6
+ if (!content.includes(old_str)) throw new Error(`str_replace failed: old_str not found in content`);
7
+ return content.replace(old_str, new_str);
8
+ }
9
+ case "append": return content + operation.content;
10
+ case "prepend": return operation.content + content;
11
+ case "insert": {
12
+ const lines = content.split("\n");
13
+ if (operation.line < 0 || operation.line > lines.length) throw new Error(`insert failed: line ${operation.line} out of range (0-${lines.length})`);
14
+ lines.splice(operation.line, 0, operation.content);
15
+ return lines.join("\n");
16
+ }
17
+ case "delete":
18
+ if (!content.includes(operation.content)) throw new Error(`delete failed: content not found`);
19
+ return content.replace(operation.content, "");
20
+ default: throw new Error(`Unknown operation type: ${operation.type}`);
21
+ }
22
+ }
23
+
24
+ //#endregion
25
+ export { applyOperation };
26
+ //# sourceMappingURL=operations.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.mjs","names":[],"sources":["../../src/resources/operations.ts"],"sourcesContent":["// Apply operations to text content\n\nimport type { Operation } from './types.js';\n\nexport function applyOperation(content: string, operation: Operation): string {\n switch (operation.type) {\n case 'str_replace': {\n const { old_str, new_str } = operation;\n if (!content.includes(old_str)) {\n throw new Error(`str_replace failed: old_str not found in content`);\n }\n return content.replace(old_str, new_str);\n }\n\n case 'append': {\n return content + operation.content;\n }\n\n case 'prepend': {\n return operation.content + content;\n }\n\n case 'insert': {\n const lines = content.split('\\n');\n if (operation.line < 0 || operation.line > lines.length) {\n throw new Error(`insert failed: line ${operation.line} out of range (0-${lines.length})`);\n }\n lines.splice(operation.line, 0, operation.content);\n return lines.join('\\n');\n }\n\n case 'delete': {\n if (!content.includes(operation.content)) {\n throw new Error(`delete failed: content not found`);\n }\n return content.replace(operation.content, '');\n }\n\n default:\n throw new Error(`Unknown operation type: ${(operation as any).type}`);\n }\n}\n"],"mappings":";AAIA,SAAgB,eAAe,SAAiB,WAA8B;AAC5E,SAAQ,UAAU,MAAlB;EACE,KAAK,eAAe;GAClB,MAAM,EAAE,SAAS,YAAY;AAC7B,OAAI,CAAC,QAAQ,SAAS,QAAQ,CAC5B,OAAM,IAAI,MAAM,mDAAmD;AAErE,UAAO,QAAQ,QAAQ,SAAS,QAAQ;;EAG1C,KAAK,SACH,QAAO,UAAU,UAAU;EAG7B,KAAK,UACH,QAAO,UAAU,UAAU;EAG7B,KAAK,UAAU;GACb,MAAM,QAAQ,QAAQ,MAAM,KAAK;AACjC,OAAI,UAAU,OAAO,KAAK,UAAU,OAAO,MAAM,OAC/C,OAAM,IAAI,MAAM,uBAAuB,UAAU,KAAK,mBAAmB,MAAM,OAAO,GAAG;AAE3F,SAAM,OAAO,UAAU,MAAM,GAAG,UAAU,QAAQ;AAClD,UAAO,MAAM,KAAK,KAAK;;EAGzB,KAAK;AACH,OAAI,CAAC,QAAQ,SAAS,UAAU,QAAQ,CACtC,OAAM,IAAI,MAAM,mCAAmC;AAErD,UAAO,QAAQ,QAAQ,UAAU,SAAS,GAAG;EAG/C,QACE,OAAM,IAAI,MAAM,2BAA4B,UAAkB,OAAO"}
@@ -0,0 +1,7 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+
3
+ //#region src/resources/resource-file.d.ts
4
+ declare function registerResourceFileResource(server: McpServer): void;
5
+ //#endregion
6
+ export { registerResourceFileResource };
7
+ //# sourceMappingURL=resource-file.d.mts.map
@@ -0,0 +1,20 @@
1
+ import { readMcpResource } from "./resource-reader.mjs";
2
+
3
+ //#region src/resources/resource-file.ts
4
+ function registerResourceFileResource(server) {
5
+ server.registerResource("Resource File", "mcp://backlog/resources/{path}", {
6
+ description: "Read a resource file",
7
+ mimeType: "text/plain"
8
+ }, async (uri) => {
9
+ const resource = await readMcpResource(uri.toString());
10
+ return { contents: [{
11
+ uri: uri.toString(),
12
+ mimeType: resource.mimeType,
13
+ text: resource.content
14
+ }] };
15
+ });
16
+ }
17
+
18
+ //#endregion
19
+ export { registerResourceFileResource };
20
+ //# sourceMappingURL=resource-file.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-file.mjs","names":[],"sources":["../../src/resources/resource-file.ts"],"sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { readMcpResource } from './resource-reader.js';\n\nexport function registerResourceFileResource(server: McpServer) {\n server.registerResource(\n 'Resource File',\n 'mcp://backlog/resources/{path}',\n { description: 'Read a resource file', mimeType: 'text/plain' },\n async (uri: URL) => {\n const resource = await readMcpResource(uri.toString());\n return { contents: [{ uri: uri.toString(), mimeType: resource.mimeType, text: resource.content }] };\n }\n );\n}\n"],"mappings":";;;AAGA,SAAgB,6BAA6B,QAAmB;AAC9D,QAAO,iBACL,iBACA,kCACA;EAAE,aAAa;EAAwB,UAAU;EAAc,EAC/D,OAAO,QAAa;EAClB,MAAM,WAAW,MAAM,gBAAgB,IAAI,UAAU,CAAC;AACtD,SAAO,EAAE,UAAU,CAAC;GAAE,KAAK,IAAI,UAAU;GAAE,UAAU,SAAS;GAAU,MAAM,SAAS;GAAS,CAAC,EAAE;GAEtG"}
@@ -0,0 +1,10 @@
1
+ //#region src/resources/resource-reader.d.ts
2
+ interface ResourceContent {
3
+ content: string;
4
+ frontmatter?: Record<string, any>;
5
+ mimeType: string;
6
+ }
7
+ declare function readMcpResource(uri: string): ResourceContent;
8
+ //#endregion
9
+ export { ResourceContent, readMcpResource };
10
+ //# sourceMappingURL=resource-reader.d.mts.map
@@ -0,0 +1,34 @@
1
+ import { resolveMcpUri } from "../utils/uri-resolver.mjs";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import matter from "gray-matter";
4
+
5
+ //#region src/resources/resource-reader.ts
6
+ function readMcpResource(uri) {
7
+ const filePath = resolveMcpUri(uri);
8
+ if (!existsSync(filePath)) throw new Error(`Resource not found: ${uri}`);
9
+ const content = readFileSync(filePath, "utf-8");
10
+ const ext = filePath.split(".").pop()?.toLowerCase() || "txt";
11
+ const mimeType = {
12
+ md: "text/markdown",
13
+ json: "application/json",
14
+ ts: "text/typescript",
15
+ js: "application/javascript",
16
+ txt: "text/plain"
17
+ }[ext] || "text/plain";
18
+ if (ext === "md") {
19
+ const parsed = matter(content);
20
+ return {
21
+ content: parsed.content,
22
+ frontmatter: parsed.data,
23
+ mimeType
24
+ };
25
+ }
26
+ return {
27
+ content,
28
+ mimeType
29
+ };
30
+ }
31
+
32
+ //#endregion
33
+ export { readMcpResource };
34
+ //# sourceMappingURL=resource-reader.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-reader.mjs","names":[],"sources":["../../src/resources/resource-reader.ts"],"sourcesContent":["import { readFileSync, existsSync } from 'node:fs';\nimport matter from 'gray-matter';\nimport { resolveMcpUri } from '../utils/uri-resolver.js';\n\nexport interface ResourceContent {\n content: string;\n frontmatter?: Record<string, any>;\n mimeType: string;\n}\n\nexport function readMcpResource(uri: string): ResourceContent {\n const filePath = resolveMcpUri(uri);\n \n if (!existsSync(filePath)) {\n throw new Error(`Resource not found: ${uri}`);\n }\n \n const content = readFileSync(filePath, 'utf-8');\n const ext = filePath.split('.').pop()?.toLowerCase() || 'txt';\n \n const mimeMap: Record<string, string> = {\n md: 'text/markdown',\n json: 'application/json',\n ts: 'text/typescript',\n js: 'application/javascript',\n txt: 'text/plain',\n };\n \n const mimeType = mimeMap[ext] || 'text/plain';\n \n // Parse frontmatter for markdown files\n if (ext === 'md') {\n const parsed = matter(content);\n return {\n content: parsed.content,\n frontmatter: parsed.data,\n mimeType,\n };\n }\n \n return {\n content,\n mimeType,\n };\n}\n"],"mappings":";;;;;AAUA,SAAgB,gBAAgB,KAA8B;CAC5D,MAAM,WAAW,cAAc,IAAI;AAEnC,KAAI,CAAC,WAAW,SAAS,CACvB,OAAM,IAAI,MAAM,uBAAuB,MAAM;CAG/C,MAAM,UAAU,aAAa,UAAU,QAAQ;CAC/C,MAAM,MAAM,SAAS,MAAM,IAAI,CAAC,KAAK,EAAE,aAAa,IAAI;CAUxD,MAAM,WARkC;EACtC,IAAI;EACJ,MAAM;EACN,IAAI;EACJ,IAAI;EACJ,KAAK;EACN,CAEwB,QAAQ;AAGjC,KAAI,QAAQ,MAAM;EAChB,MAAM,SAAS,OAAO,QAAQ;AAC9B,SAAO;GACL,SAAS,OAAO;GAChB,aAAa,OAAO;GACpB;GACD;;AAGH,QAAO;EACL;EACA;EACD"}
@@ -0,0 +1,7 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+
3
+ //#region src/resources/task-attached.d.ts
4
+ declare function registerTaskAttachedResource(server: McpServer): void;
5
+ //#endregion
6
+ export { registerTaskAttachedResource };
7
+ //# sourceMappingURL=task-attached.d.mts.map
@@ -0,0 +1,17 @@
1
+ import { readMcpResource } from "./resource-reader.mjs";
2
+
3
+ //#region src/resources/task-attached.ts
4
+ function registerTaskAttachedResource(server) {
5
+ server.registerResource("Task-Attached Resource", "mcp://backlog/resources/{taskId}/{filename}", { description: "Task-attached resources (ADRs, design docs, etc.)" }, async (uri) => {
6
+ const { content, mimeType } = await readMcpResource(uri.toString());
7
+ return { contents: [{
8
+ uri: uri.toString(),
9
+ mimeType,
10
+ text: content
11
+ }] };
12
+ });
13
+ }
14
+
15
+ //#endregion
16
+ export { registerTaskAttachedResource };
17
+ //# sourceMappingURL=task-attached.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-attached.mjs","names":[],"sources":["../../src/resources/task-attached.ts"],"sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { readMcpResource } from './resource-reader.js';\n\nexport function registerTaskAttachedResource(server: McpServer) {\n server.registerResource(\n 'Task-Attached Resource',\n 'mcp://backlog/resources/{taskId}/{filename}',\n { description: 'Task-attached resources (ADRs, design docs, etc.)' },\n async (uri: URL) => {\n const { content, mimeType } = await readMcpResource(uri.toString());\n return { contents: [{ uri: uri.toString(), mimeType, text: content }] };\n }\n );\n}\n"],"mappings":";;;AAGA,SAAgB,6BAA6B,QAAmB;AAC9D,QAAO,iBACL,0BACA,+CACA,EAAE,aAAa,qDAAqD,EACpE,OAAO,QAAa;EAClB,MAAM,EAAE,SAAS,aAAa,MAAM,gBAAgB,IAAI,UAAU,CAAC;AACnE,SAAO,EAAE,UAAU,CAAC;GAAE,KAAK,IAAI,UAAU;GAAE;GAAU,MAAM;GAAS,CAAC,EAAE;GAE1E"}
@@ -0,0 +1,7 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+
3
+ //#region src/resources/task-by-id.d.ts
4
+ declare function registerTaskByIdResource(server: McpServer): void;
5
+ //#endregion
6
+ export { registerTaskByIdResource };
7
+ //# sourceMappingURL=task-by-id.d.mts.map
@@ -0,0 +1,23 @@
1
+ import { storage } from "../storage/backlog.mjs";
2
+
3
+ //#region src/resources/task-by-id.ts
4
+ function registerTaskByIdResource(server) {
5
+ server.registerResource("Task by ID", "mcp://backlog/tasks/{taskId}/file", {
6
+ description: "Get a specific task",
7
+ mimeType: "text/markdown"
8
+ }, async (uri) => {
9
+ const match = uri.toString().match(/mcp:\/\/backlog\/tasks\/([^/]+)\/file/);
10
+ if (!match || !match[1]) throw new Error("Invalid URI");
11
+ const markdown = storage.getMarkdown(match[1]);
12
+ if (!markdown) throw new Error("Task not found");
13
+ return { contents: [{
14
+ uri: uri.toString(),
15
+ mimeType: "text/markdown",
16
+ text: markdown
17
+ }] };
18
+ });
19
+ }
20
+
21
+ //#endregion
22
+ export { registerTaskByIdResource };
23
+ //# sourceMappingURL=task-by-id.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-by-id.mjs","names":[],"sources":["../../src/resources/task-by-id.ts"],"sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { storage } from '../storage/backlog.js';\n\nexport function registerTaskByIdResource(server: McpServer) {\n server.registerResource(\n 'Task by ID',\n 'mcp://backlog/tasks/{taskId}/file',\n { description: 'Get a specific task', mimeType: 'text/markdown' },\n async (uri: URL) => {\n const match = uri.toString().match(/mcp:\\/\\/backlog\\/tasks\\/([^/]+)\\/file/);\n if (!match || !match[1]) throw new Error('Invalid URI');\n const markdown = storage.getMarkdown(match[1]);\n if (!markdown) throw new Error('Task not found');\n return { contents: [{ uri: uri.toString(), mimeType: 'text/markdown', text: markdown }] };\n }\n );\n}\n"],"mappings":";;;AAGA,SAAgB,yBAAyB,QAAmB;AAC1D,QAAO,iBACL,cACA,qCACA;EAAE,aAAa;EAAuB,UAAU;EAAiB,EACjE,OAAO,QAAa;EAClB,MAAM,QAAQ,IAAI,UAAU,CAAC,MAAM,wCAAwC;AAC3E,MAAI,CAAC,SAAS,CAAC,MAAM,GAAI,OAAM,IAAI,MAAM,cAAc;EACvD,MAAM,WAAW,QAAQ,YAAY,MAAM,GAAG;AAC9C,MAAI,CAAC,SAAU,OAAM,IAAI,MAAM,iBAAiB;AAChD,SAAO,EAAE,UAAU,CAAC;GAAE,KAAK,IAAI,UAAU;GAAE,UAAU;GAAiB,MAAM;GAAU,CAAC,EAAE;GAE5F"}
@@ -0,0 +1,7 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+
3
+ //#region src/resources/tasks.d.ts
4
+ declare function registerTasksResource(server: McpServer): void;
5
+ //#endregion
6
+ export { registerTasksResource };
7
+ //# sourceMappingURL=tasks.d.mts.map
@@ -0,0 +1,20 @@
1
+ import { storage } from "../storage/backlog.mjs";
2
+
3
+ //#region src/resources/tasks.ts
4
+ function registerTasksResource(server) {
5
+ server.registerResource("All Tasks", "mcp://backlog/tasks", {
6
+ description: "List of all tasks",
7
+ mimeType: "application/json"
8
+ }, async () => {
9
+ const tasks = storage.list({});
10
+ return { contents: [{
11
+ uri: "mcp://backlog/tasks",
12
+ mimeType: "application/json",
13
+ text: JSON.stringify(tasks, null, 2)
14
+ }] };
15
+ });
16
+ }
17
+
18
+ //#endregion
19
+ export { registerTasksResource };
20
+ //# sourceMappingURL=tasks.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.mjs","names":[],"sources":["../../src/resources/tasks.ts"],"sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { storage } from '../storage/backlog.js';\n\nexport function registerTasksResource(server: McpServer) {\n server.registerResource(\n 'All Tasks',\n 'mcp://backlog/tasks',\n { description: 'List of all tasks', mimeType: 'application/json' },\n async () => {\n const tasks = storage.list({});\n return { contents: [{ uri: 'mcp://backlog/tasks', mimeType: 'application/json', text: JSON.stringify(tasks, null, 2) }] };\n }\n );\n}\n"],"mappings":";;;AAGA,SAAgB,sBAAsB,QAAmB;AACvD,QAAO,iBACL,aACA,uBACA;EAAE,aAAa;EAAqB,UAAU;EAAoB,EAClE,YAAY;EACV,MAAM,QAAQ,QAAQ,KAAK,EAAE,CAAC;AAC9B,SAAO,EAAE,UAAU,CAAC;GAAE,KAAK;GAAuB,UAAU;GAAoB,MAAM,KAAK,UAAU,OAAO,MAAM,EAAE;GAAE,CAAC,EAAE;GAE5H"}
@@ -0,0 +1,33 @@
1
+ //#region src/resources/types.d.ts
2
+ type OperationType = 'str_replace' | 'append' | 'prepend' | 'insert' | 'delete';
3
+ interface StrReplaceOperation {
4
+ type: 'str_replace';
5
+ old_str: string;
6
+ new_str: string;
7
+ }
8
+ interface AppendOperation {
9
+ type: 'append';
10
+ content: string;
11
+ }
12
+ interface PrependOperation {
13
+ type: 'prepend';
14
+ content: string;
15
+ }
16
+ interface InsertOperation {
17
+ type: 'insert';
18
+ line: number;
19
+ content: string;
20
+ }
21
+ interface DeleteOperation {
22
+ type: 'delete';
23
+ content: string;
24
+ }
25
+ type Operation = StrReplaceOperation | AppendOperation | PrependOperation | InsertOperation | DeleteOperation;
26
+ interface WriteResourceResult {
27
+ success: boolean;
28
+ message: string;
29
+ error?: string;
30
+ }
31
+ //#endregion
32
+ export { AppendOperation, DeleteOperation, InsertOperation, Operation, OperationType, PrependOperation, StrReplaceOperation, WriteResourceResult };
33
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,11 @@
1
+ //#region src/resources/uri.d.ts
2
+ interface ParsedURI {
3
+ server: string;
4
+ resource: string;
5
+ taskId?: string;
6
+ field?: string;
7
+ }
8
+ declare function parseURI(uri: string): ParsedURI | null;
9
+ //#endregion
10
+ export { ParsedURI, parseURI };
11
+ //# sourceMappingURL=uri.d.mts.map
@@ -0,0 +1,22 @@
1
+ //#region src/resources/uri.ts
2
+ function parseURI(uri) {
3
+ const match = uri.match(/^mcp:\/\/([^\/]+)\/(.+)$/);
4
+ if (!match) return null;
5
+ const [, server, resource] = match;
6
+ if (!server || !resource) return null;
7
+ const taskMatch = resource.match(/^tasks\/([^\/]+)(?:\/(description|file))?$/);
8
+ if (taskMatch) return {
9
+ server,
10
+ resource,
11
+ taskId: taskMatch[1],
12
+ field: taskMatch[2] || "file"
13
+ };
14
+ return {
15
+ server,
16
+ resource
17
+ };
18
+ }
19
+
20
+ //#endregion
21
+ export { parseURI };
22
+ //# sourceMappingURL=uri.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uri.mjs","names":[],"sources":["../../src/resources/uri.ts"],"sourcesContent":["// URI parser for mcp:// scheme\n\nexport interface ParsedURI {\n server: string;\n resource: string;\n taskId?: string;\n field?: string;\n}\n\nexport function parseURI(uri: string): ParsedURI | null {\n // Expected format: mcp://backlog/{resource}\n const match = uri.match(/^mcp:\\/\\/([^\\/]+)\\/(.+)$/);\n if (!match) return null;\n\n const [, server, resource] = match;\n \n if (!server || !resource) return null;\n \n // Check if it's a task field edit: tasks/{id}/description or tasks/{id}/file\n const taskMatch = resource.match(/^tasks\\/([^\\/]+)(?:\\/(description|file))?$/);\n if (taskMatch) {\n return {\n server,\n resource,\n taskId: taskMatch[1],\n field: taskMatch[2] || 'file',\n };\n }\n\n // General resource (artifacts, resources, etc.)\n return {\n server,\n resource,\n };\n}\n"],"mappings":";AASA,SAAgB,SAAS,KAA+B;CAEtD,MAAM,QAAQ,IAAI,MAAM,2BAA2B;AACnD,KAAI,CAAC,MAAO,QAAO;CAEnB,MAAM,GAAG,QAAQ,YAAY;AAE7B,KAAI,CAAC,UAAU,CAAC,SAAU,QAAO;CAGjC,MAAM,YAAY,SAAS,MAAM,6CAA6C;AAC9E,KAAI,UACF,QAAO;EACL;EACA;EACA,QAAQ,UAAU;EAClB,OAAO,UAAU,MAAM;EACxB;AAIH,QAAO;EACL;EACA;EACD"}