@kamkom/awb-mcp 0.0.1
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/.ai/backlog.md +435 -0
- package/.ai/prd.md +215 -0
- package/.cursor/commands/implement-us.md +24 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -0
- package/src/index.ts +142 -0
- package/tsconfig.json +17 -0
package/.ai/backlog.md
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
# AWB MCP — Product Backlog
|
|
2
|
+
|
|
3
|
+
Stories and acceptance criteria derived from [.ai/prd.md](.ai/prd.md), grouped by milestone.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## v0.1 — MVP core
|
|
8
|
+
|
|
9
|
+
### Story 1: MCP server runs in VS Code over stdio
|
|
10
|
+
|
|
11
|
+
**User story**
|
|
12
|
+
|
|
13
|
+
- As a developer using VS Code with an MCP-capable agent/chat client
|
|
14
|
+
- I want to run the AWB MCP server as a stdio-based server so that I can use its tools from my editor without extra infrastructure
|
|
15
|
+
|
|
16
|
+
**Acceptance criteria**
|
|
17
|
+
|
|
18
|
+
- Given a Windows or macOS machine with Node.js and VS Code configured for MCP
|
|
19
|
+
- When I install the package (e.g. `npm i -g awb-mcp`) and add the server to my MCP settings with the command path
|
|
20
|
+
- Then the server starts over stdio, responds to MCP protocol messages, and exposes tools (e.g. list_tools returns AWB tools)
|
|
21
|
+
|
|
22
|
+
**Edge cases**
|
|
23
|
+
|
|
24
|
+
- Linux: document as optional; no hard requirement to fix Linux-only issues in v0.1
|
|
25
|
+
- Invalid or missing `rootPath`: server returns clear error, does not crash
|
|
26
|
+
- Server receives malformed or unexpected JSON: responds with protocol error, does not exit ungracefully
|
|
27
|
+
|
|
28
|
+
**Technical considerations**
|
|
29
|
+
|
|
30
|
+
- Use `@modelcontextprotocol/sdk` StdioServerTransport; single executable entrypoint (e.g. `awb-mcp` or `npx awb-mcp`)
|
|
31
|
+
- No network listener; stdio only
|
|
32
|
+
- Package `bin` in package.json so `awb-mcp` is on PATH after global install
|
|
33
|
+
|
|
34
|
+
**Design notes**
|
|
35
|
+
|
|
36
|
+
- Keep VS Code MCP config minimal: only the command (e.g. `awb-mcp` or `npx awb-mcp`). No required env vars or args for basic use
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
### Story 2: plan_bootstrap returns a deterministic plan with full contents and warnings
|
|
41
|
+
|
|
42
|
+
**User story**
|
|
43
|
+
|
|
44
|
+
- As a user in an agentic workflow
|
|
45
|
+
- I want to call `awb.plan_bootstrap` with a repo root and options so that I get a clear, deterministic plan of file operations (create/update/skip) with full file contents and warnings for existing files
|
|
46
|
+
|
|
47
|
+
**Acceptance criteria**
|
|
48
|
+
|
|
49
|
+
- Given a valid `rootPath` (existing directory) and optional `options` (mode, includeReadmeProposal, overwritePolicy, files allowlist)
|
|
50
|
+
- When I call the tool `awb.plan_bootstrap` with those inputs
|
|
51
|
+
- Then I receive a response containing: a unique `planId`; an `operations[]` array with for each path: `path`, `action` (CREATE | UPDATE_MANAGED | SKIP), `content` when action is CREATE or UPDATE_MANAGED, `reason` (especially for SKIP), and `warnings[]`; optional `repoHints` and `readmeProposal` when requested
|
|
52
|
+
|
|
53
|
+
- Given a path that does not exist on disk
|
|
54
|
+
- When the plan is computed
|
|
55
|
+
- Then that path has action CREATE and includes full static template content
|
|
56
|
+
|
|
57
|
+
- Given a path that exists and has no AWB managed block and overwritePolicy is not "overwrite"
|
|
58
|
+
- When the plan is computed
|
|
59
|
+
- Then that path has action SKIP, a reason, and a warning that the file exists and is not being overwritten
|
|
60
|
+
|
|
61
|
+
**Edge cases**
|
|
62
|
+
|
|
63
|
+
- `rootPath` is a file or non-existent: return clear error, no plan
|
|
64
|
+
- `files` allowlist: only those paths appear in operations; others are omitted
|
|
65
|
+
- Empty repo (no existing scaffold): all scaffold paths are CREATE
|
|
66
|
+
- overwritePolicy `"skip"`: existing files always SKIP (even if they contain a managed block). `"managed-only"`: SKIP unless file has AWB managed block (then UPDATE_MANAGED)
|
|
67
|
+
|
|
68
|
+
**Technical considerations**
|
|
69
|
+
|
|
70
|
+
- planId: UUID or timestamp-based unique string; same inputs produce same plan content (deterministic)
|
|
71
|
+
- Read only: directory listing and file existence (and optionally small config files for hints). No writes in plan
|
|
72
|
+
- Input schema: validate rootPath (string), options optional with zod or equivalent
|
|
73
|
+
|
|
74
|
+
**Design notes**
|
|
75
|
+
|
|
76
|
+
- Static mode default: all templates are prefilled content from server bundle. No LLM calls in server
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### Story 3: apply_bootstrap applies an approved plan with atomic writes and conflict handling
|
|
81
|
+
|
|
82
|
+
**User story**
|
|
83
|
+
|
|
84
|
+
- As a user who has reviewed a bootstrap plan
|
|
85
|
+
- I want to call `awb.apply_bootstrap` with the planId and operations so that only approved changes are written to disk using atomic writes and explicit conflict handling
|
|
86
|
+
|
|
87
|
+
**Acceptance criteria**
|
|
88
|
+
|
|
89
|
+
- Given a valid `rootPath`, the `planId` from a prior `plan_bootstrap` call, and the same `operations[]` (or server-validated subset)
|
|
90
|
+
- When I call `awb.apply_bootstrap` without dryRun
|
|
91
|
+
- Then each CREATE/UPDATE_MANAGED operation is written to disk; each SKIP is not written; the response includes `results[]` with per-path `status` (WRITTEN | SKIPPED | FAILED) and `details`
|
|
92
|
+
|
|
93
|
+
- Given applyOptions.dryRun is true
|
|
94
|
+
- When I call `awb.apply_bootstrap`
|
|
95
|
+
- Then no files are modified and the response describes what would have been done (e.g. same result structure with WRITTEN/SKIPPED/FAILED as if applied)
|
|
96
|
+
|
|
97
|
+
- Given writeMode is "atomic" (default)
|
|
98
|
+
- When a file is written
|
|
99
|
+
- Then content is written to a temp file in the same directory and then renamed into place so that partial writes are not visible
|
|
100
|
+
|
|
101
|
+
**Edge cases**
|
|
102
|
+
|
|
103
|
+
- planId or operations do not match a recent plan: server may validate and return FAILED for mismatches if failOnConflict is true
|
|
104
|
+
- Disk full or permission error: that path returns status FAILED with details; other paths still attempted where possible
|
|
105
|
+
- apply_bootstrap called with operations from a different rootPath: server validates and rejects or marks FAILED
|
|
106
|
+
|
|
107
|
+
**Technical considerations**
|
|
108
|
+
|
|
109
|
+
- Atomic write: write to `*.tmp` (or similar) in same directory, fs.rename to final path
|
|
110
|
+
- Idempotency: applying the same plan twice with same overwrite policy should yield same outcome (second run no unexpected changes)
|
|
111
|
+
- applyOptions: dryRun, writeMode "atomic", failOnConflict (default true)
|
|
112
|
+
|
|
113
|
+
**Design notes**
|
|
114
|
+
|
|
115
|
+
- Server does not re-read disk to “re-plan”; it applies the operations as given, enforcing overwrite policy per operation
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
### Story 4: Overwrite and idempotency — SKIP by default, managed-block and explicit overwrite
|
|
120
|
+
|
|
121
|
+
**User story**
|
|
122
|
+
|
|
123
|
+
- As a user running bootstrap in a repo that may already have some files
|
|
124
|
+
- I want the server to never overwrite my content unless a file is under AWB management or I explicitly allow overwrite so that I do not lose work
|
|
125
|
+
|
|
126
|
+
**Acceptance criteria**
|
|
127
|
+
|
|
128
|
+
- Given a target path where the file does not exist
|
|
129
|
+
- When the plan is computed and applied
|
|
130
|
+
- Then the action is CREATE and the file is created
|
|
131
|
+
|
|
132
|
+
- Given a target path where the file exists and does not contain the AWB managed block marker
|
|
133
|
+
- When overwritePolicy is "skip" or "managed-only" (default)
|
|
134
|
+
- Then the action is SKIP, reason and warnings are set, and the file is not modified on apply
|
|
135
|
+
|
|
136
|
+
- Given a target path where the file exists and contains the AWB managed block marker
|
|
137
|
+
- When overwritePolicy is "managed-only" (default)
|
|
138
|
+
- Then the action is UPDATE_MANAGED, content is the new managed block content, and on apply only the managed section is updated (or full file replace per implementation)
|
|
139
|
+
|
|
140
|
+
- Given the user passes overwrite: true for a specific file (or overwritePolicy "overwrite")
|
|
141
|
+
- When the plan is computed and applied
|
|
142
|
+
- Then that path can be CREATE/UPDATE and existing content may be replaced; user has explicitly opted in
|
|
143
|
+
|
|
144
|
+
**Edge cases**
|
|
145
|
+
|
|
146
|
+
- Managed block marker missing or malformed: treat as unmanaged, SKIP unless overwrite
|
|
147
|
+
- Partial file (e.g. only start marker): define behavior (e.g. SKIP or treat as managed); document
|
|
148
|
+
|
|
149
|
+
**Technical considerations**
|
|
150
|
+
|
|
151
|
+
- Define a single, documented AWB managed block format (e.g. `<!-- AWB MANAGED BEGIN -->` … `<!-- AWB MANAGED END -->` or similar)
|
|
152
|
+
- plan_bootstrap decides CREATE | UPDATE_MANAGED | SKIP per path based on existence and overwrite policy; apply_bootstrap enforces the same policy
|
|
153
|
+
|
|
154
|
+
**Design notes**
|
|
155
|
+
|
|
156
|
+
- Safe-by-default: “plan first, then apply” with clear SKIP behavior and no blind overwrites
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### Story 5: Baseline scaffold — static templates for .github and AGENTS.md
|
|
161
|
+
|
|
162
|
+
**User story**
|
|
163
|
+
|
|
164
|
+
- As a user bootstrapping a repo for agentic workflows
|
|
165
|
+
- I want the server to generate a fixed set of high-quality, generic template files so that my repo has a standard structure for instructions, prompts, and agents without me creating them by hand
|
|
166
|
+
|
|
167
|
+
**Acceptance criteria**
|
|
168
|
+
|
|
169
|
+
- Given a successful `plan_bootstrap` in static mode with no files allowlist (or allowlist including the baseline paths)
|
|
170
|
+
- When the plan is generated
|
|
171
|
+
- Then operations include the full baseline tree: `.github/copilot-instructions.md`, `.github/instructions/general.instructions.md`, `.github/instructions/testing.instructions.md`, `.github/prompts/readme.prompt.md`, `.github/prompts/onboarding.prompt.md`, `.github/prompts/change-summary.prompt.md`, `.github/agents/architect.agent.md`, `.github/agents/implementer.agent.md`, `.github/agents/reviewer.agent.md`, `AGENTS.md`
|
|
172
|
+
|
|
173
|
+
- Given the template content returned in the plan
|
|
174
|
+
- When I read it
|
|
175
|
+
- Then it is generic, high-signal, and safe: emphasizes “don’t guess,” “prefer repo scripts,” “write tests,” “avoid destructive commands”
|
|
176
|
+
- And placeholders (e.g. `{{packageManager}}`, `{{testCommand}}`) are minimal and optional; may be left as defaults in v0.1
|
|
177
|
+
|
|
178
|
+
**Edge cases**
|
|
179
|
+
|
|
180
|
+
- `files` allowlist provided: only listed paths that belong to the baseline appear in operations
|
|
181
|
+
- Paths outside repo root (e.g. `..` in path): server rejects or normalizes
|
|
182
|
+
|
|
183
|
+
**Technical considerations**
|
|
184
|
+
|
|
185
|
+
- Templates shipped as static files in the server package (e.g. in a `templates/` directory); no runtime fetch
|
|
186
|
+
- Use path.join(rootPath, ...) for all paths; normalize and reject paths not under rootPath
|
|
187
|
+
|
|
188
|
+
**Design notes**
|
|
189
|
+
|
|
190
|
+
- Templates work for any repo/stack; no stack-specific logic required in v0.1
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
### Story 6: Safety — no network, no shell, minimal reads, atomic writes
|
|
195
|
+
|
|
196
|
+
**User story**
|
|
197
|
+
|
|
198
|
+
- As a user running the AWB MCP server
|
|
199
|
+
- I want it to never perform network calls, shell execution, or broad filesystem reads so that I can trust it in sensitive or offline environments
|
|
200
|
+
|
|
201
|
+
**Acceptance criteria**
|
|
202
|
+
|
|
203
|
+
- Given the server is running
|
|
204
|
+
- When any tool is invoked
|
|
205
|
+
- Then the server does not open outbound network connections or spawn shell/child processes
|
|
206
|
+
|
|
207
|
+
- Given plan_bootstrap or apply_bootstrap is called
|
|
208
|
+
- When the server needs to check files
|
|
209
|
+
- Then it only checks existence of target paths and, if needed, reads small config files (e.g. package.json) for hints; it does not read arbitrary or sensitive paths beyond what is documented
|
|
210
|
+
|
|
211
|
+
- Given apply_bootstrap writes a file
|
|
212
|
+
- When writeMode is atomic
|
|
213
|
+
- Then content is written to a temp file and then renamed; no partial content is visible at the final path on failure
|
|
214
|
+
|
|
215
|
+
- Given a file already exists and policy says SKIP
|
|
216
|
+
- When apply_bootstrap runs
|
|
217
|
+
- Then the user sees clear warnings in the plan and the apply result reflects SKIPPED with a reason
|
|
218
|
+
|
|
219
|
+
**Edge cases**
|
|
220
|
+
|
|
221
|
+
- Symlinks: document whether we follow or reject; prefer not following symlinks outside rootPath
|
|
222
|
+
- Very long paths or huge files: avoid reading large files; existence check only where sufficient
|
|
223
|
+
|
|
224
|
+
**Technical considerations**
|
|
225
|
+
|
|
226
|
+
- No require('child_process') or fetch/axios; no external HTTP
|
|
227
|
+
- Atomic write: temp file in same directory as target, then rename
|
|
228
|
+
- Explicit allowlist of files that may be read for hints (e.g. package.json, pyproject.toml) in v0.2
|
|
229
|
+
|
|
230
|
+
**Design notes**
|
|
231
|
+
|
|
232
|
+
- Security section of PRD is non-negotiable for MVP; tests or checklist can enforce “no network, no shell”
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
### Story 7: Early deployment and validation — works on other computers
|
|
237
|
+
|
|
238
|
+
**User story**
|
|
239
|
+
|
|
240
|
+
- As a maintainer or a user trying the package on a clean machine
|
|
241
|
+
- I want a clear packaging, install, and validation flow so that we can confirm the server runs on other computers (Windows and macOS) and works with VS Code MCP
|
|
242
|
+
|
|
243
|
+
**Acceptance criteria**
|
|
244
|
+
|
|
245
|
+
- Given the project is built and published (e.g. to npm as `awb-mcp` or installed from tarball)
|
|
246
|
+
- When another user on a clean Windows or macOS machine runs `npm i -g awb-mcp` (or equivalent)
|
|
247
|
+
- Then the install completes and the `awb-mcp` (or documented) command is on PATH
|
|
248
|
+
|
|
249
|
+
- Given the server is installed globally
|
|
250
|
+
- When the user adds the server to VS Code MCP settings (e.g. in settings.json or Cursor MCP config) with only the command path
|
|
251
|
+
- Then VS Code/Cursor can start the server over stdio and list tools
|
|
252
|
+
|
|
253
|
+
- Given the server is running in VS Code
|
|
254
|
+
- When the user (or a script) invokes `awb.plan_bootstrap` with a valid repo root
|
|
255
|
+
- Then the tool returns a valid plan (planId, operations array, no crash)
|
|
256
|
+
|
|
257
|
+
- Given a release checklist exists (in repo or docs)
|
|
258
|
+
- When preparing a release
|
|
259
|
+
- Then the checklist includes: build, pack or publish, install on clean Windows, install on clean macOS, configure VS Code MCP, run smoke test (plan_bootstrap + optional apply_bootstrap in a temp dir)
|
|
260
|
+
|
|
261
|
+
**Edge cases**
|
|
262
|
+
|
|
263
|
+
- Node version: document minimum (e.g. Node 18+); package.json engines optional
|
|
264
|
+
- npx vs global install: both documented; smoke test covers at least one
|
|
265
|
+
|
|
266
|
+
**Technical considerations**
|
|
267
|
+
|
|
268
|
+
- package.json: `bin` entry for the executable; `main` or type module and entrypoint that runs the MCP server
|
|
269
|
+
- Build step that produces runnable code (e.g. tsc + node, or tsx in bin); ensure dependencies are bundled or declared so install is self-contained
|
|
270
|
+
- Optional: simple smoke script (e.g. node -e "call MCP list_tools") or doc that says “run plan_bootstrap from client”
|
|
271
|
+
|
|
272
|
+
**Design notes**
|
|
273
|
+
|
|
274
|
+
- Early deployment is crucial: first milestone should include “works on another machine” validation; checklist prevents regressions
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## v0.2 — Repo hints and README proposal
|
|
279
|
+
|
|
280
|
+
### Story 8: detect_repo_hints and placeholder fill
|
|
281
|
+
|
|
282
|
+
**User story**
|
|
283
|
+
|
|
284
|
+
- As a user with an existing repo (e.g. with package.json or pyproject.toml)
|
|
285
|
+
- I want the server to run lightweight heuristics to detect hints (e.g. package manager, test command) so that bootstrap templates can be filled with sensible defaults without deep stack detection
|
|
286
|
+
|
|
287
|
+
**Acceptance criteria**
|
|
288
|
+
|
|
289
|
+
- Given a repo root that contains one or more of: package.json, lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml), pyproject.toml
|
|
290
|
+
- When `awb.detect_repo_hints` (or plan_bootstrap with hint detection) runs
|
|
291
|
+
- Then the result includes a small `repoHints` object with fields such as preferred package manager, test command placeholder, or similar; no heavy or framework-specific detection
|
|
292
|
+
|
|
293
|
+
- Given repoHints are available
|
|
294
|
+
- When plan_bootstrap returns template content
|
|
295
|
+
- Then optional placeholders (e.g. `{{packageManager}}`, `{{testCommand}}`) are replaced with hint values where possible; otherwise left as safe defaults
|
|
296
|
+
|
|
297
|
+
- Given a repo with no known config files
|
|
298
|
+
- When detect runs
|
|
299
|
+
- Then repoHints is empty or has defaults; no error
|
|
300
|
+
|
|
301
|
+
**Edge cases**
|
|
302
|
+
|
|
303
|
+
- Multiple lockfiles: define precedence (e.g. pnpm > yarn > npm) and document
|
|
304
|
+
- Malformed package.json: do not throw; return partial or empty hints
|
|
305
|
+
- Must not become a “stack detection framework”: only a small, fixed set of heuristics
|
|
306
|
+
|
|
307
|
+
**Technical considerations**
|
|
308
|
+
|
|
309
|
+
- detect_repo_hints: read only package.json, pyproject.toml, lockfile presence; no install or parse of dependencies
|
|
310
|
+
- Keep logic in one place; call from plan_bootstrap when options request hints
|
|
311
|
+
|
|
312
|
+
**Design notes**
|
|
313
|
+
|
|
314
|
+
- v0.2 scope: minimal placeholders; avoid scope creep into full stack detection
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### Story 9: Optional README proposal (separate file or patch, no overwrite by default)
|
|
319
|
+
|
|
320
|
+
**User story**
|
|
321
|
+
|
|
322
|
+
- As a user who has an existing README.md
|
|
323
|
+
- I want the server to optionally suggest README content or a patch (e.g. README.agentic.md or a patch output) so that I can choose whether to integrate it without ever overwriting README.md by default
|
|
324
|
+
|
|
325
|
+
**Acceptance criteria**
|
|
326
|
+
|
|
327
|
+
- Given plan_bootstrap is called with includeReadmeProposal: false (default)
|
|
328
|
+
- When the plan is returned
|
|
329
|
+
- Then no readmeProposal field or README-related write is included; README.md is never in operations as an overwrite
|
|
330
|
+
|
|
331
|
+
- Given plan_bootstrap is called with includeReadmeProposal: true
|
|
332
|
+
- When the plan is returned
|
|
333
|
+
- Then the response includes a readmeProposal (e.g. suggested content or a separate file path like README.agentic.md or a patch); README.md is still not in operations unless user explicitly opts in elsewhere
|
|
334
|
+
- And the client (LLM/user) decides whether to apply any README changes
|
|
335
|
+
|
|
336
|
+
- Given the server suggests README content
|
|
337
|
+
- When the suggestion is returned
|
|
338
|
+
- Then it is clearly a proposal (e.g. “suggested addition” or separate file); default behavior never modifies README.md
|
|
339
|
+
|
|
340
|
+
**Edge cases**
|
|
341
|
+
|
|
342
|
+
- No README.md exists: readmeProposal can still suggest initial README content
|
|
343
|
+
- Very long README: server may suggest a patch or appendix only; avoid loading huge files unnecessarily
|
|
344
|
+
|
|
345
|
+
**Technical considerations**
|
|
346
|
+
|
|
347
|
+
- readmeProposal: string content or { path, content } for a separate file; optional patch format
|
|
348
|
+
- If README.md is ever in operations, it is only when overwritePolicy or explicit overwrite is set and documented
|
|
349
|
+
|
|
350
|
+
**Design notes**
|
|
351
|
+
|
|
352
|
+
- Narrow MVP: README handling is optional and safe-by-default; no automatic overwrite
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## v0.3 — Prompt mode and optional resources (if needed)
|
|
357
|
+
|
|
358
|
+
### Story 10: Prompt mode — server returns prompts and skeletons, client LLM refines
|
|
359
|
+
|
|
360
|
+
**User story**
|
|
361
|
+
|
|
362
|
+
- As a user who wants AI-generated bootstrap content tailored to my repo
|
|
363
|
+
- I want the server to support a “prompt” mode where it returns prompts and minimal skeleton files so that my client LLM (e.g. Copilot) can refine content before I apply it, without the server calling any external LLM
|
|
364
|
+
|
|
365
|
+
**Acceptance criteria**
|
|
366
|
+
|
|
367
|
+
- Given plan_bootstrap is called with mode: "prompt"
|
|
368
|
+
- When the plan is computed
|
|
369
|
+
- Then the server returns either (a) prompts plus minimal skeleton file content, or (b) static templates with a note that the client LLM may refine them before apply
|
|
370
|
+
- And the server does not call any third-party LLM API; any generation is done by the client using the user’s model
|
|
371
|
+
|
|
372
|
+
- Given the client has refined or generated content
|
|
373
|
+
- When the user calls apply_bootstrap with the modified plan/operations
|
|
374
|
+
- Then the server applies the provided content with the same overwrite and atomic-write rules as static mode
|
|
375
|
+
|
|
376
|
+
**Edge cases**
|
|
377
|
+
|
|
378
|
+
- mode "prompt" with no client LLM: user can still apply skeletons as-is
|
|
379
|
+
- Mixed mode (some static, some prompt): v0.3 can define per-file or global; keep simple
|
|
380
|
+
|
|
381
|
+
**Technical considerations**
|
|
382
|
+
|
|
383
|
+
- No fetch to OpenAI/Anthropic/etc. in server; prompts are strings returned to client
|
|
384
|
+
- Client is responsible for calling LLM and passing back refined content in the plan/operations
|
|
385
|
+
|
|
386
|
+
**Design notes**
|
|
387
|
+
|
|
388
|
+
- MVP: prompt mode is optional; static mode remains default and primary
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
### Story 11: Optional MCP prompts and resources for client workflows
|
|
393
|
+
|
|
394
|
+
**User story**
|
|
395
|
+
|
|
396
|
+
- As a user or client LLM
|
|
397
|
+
- I want to discover bootstrap-related prompts and read template resources via MCP so that I can run guided workflows (e.g. “bootstrap_agentic_repo”) or preview templates without calling tools
|
|
398
|
+
|
|
399
|
+
**Acceptance criteria**
|
|
400
|
+
|
|
401
|
+
- Given the server supports optional Prompts
|
|
402
|
+
- When the client lists prompts
|
|
403
|
+
- Then at least one prompt is available (e.g. bootstrap_agentic_repo) that instructs the client to call plan_bootstrap, review output, then call apply_bootstrap
|
|
404
|
+
|
|
405
|
+
- Given the server supports optional Resources
|
|
406
|
+
- When the client requests a resource URI (e.g. awb://templates/copilot-instructions.md or awb://templates/AGENTS.md)
|
|
407
|
+
- Then the server returns read-only template content for that path
|
|
408
|
+
|
|
409
|
+
- Given tools already return full template content in plan_bootstrap
|
|
410
|
+
- When implementing resources
|
|
411
|
+
- Then resources are optional; same content can be exposed via tools only
|
|
412
|
+
|
|
413
|
+
**Edge cases**
|
|
414
|
+
|
|
415
|
+
- Client does not support prompts/resources: server still works with tools only
|
|
416
|
+
- Unknown resource URI: return 404 or equivalent error
|
|
417
|
+
|
|
418
|
+
**Technical considerations**
|
|
419
|
+
|
|
420
|
+
- MCP Prompts: register with the SDK; bootstrap_agentic_repo and optionally draft_readme_patch
|
|
421
|
+
- MCP Resources: register template URIs; resolve to static files from package
|
|
422
|
+
|
|
423
|
+
**Design notes**
|
|
424
|
+
|
|
425
|
+
- v0.3 “only if needed”; if ship is faster without resources, tools-only is acceptable
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## Coverage and consistency
|
|
430
|
+
|
|
431
|
+
- All PRD MVP goals and tool contracts (plan_bootstrap, apply_bootstrap, overwrite rules, baseline scaffold, safety, distribution) are covered by v0.1 stories.
|
|
432
|
+
- v0.2 covers detect_repo_hints and README proposal.
|
|
433
|
+
- v0.3 covers prompt mode and optional prompts/resources.
|
|
434
|
+
- Early deployment (Story 7) is part of v0.1 so that “works on other computers” is validated before or with the first usable release.
|
|
435
|
+
- Acceptance criteria are written in Given/When/Then form and are testable (manual or automated).
|
package/.ai/prd.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# PRD — Agentic Bootstrap MCP Server (working name)
|
|
2
|
+
|
|
3
|
+
## 1) Summary
|
|
4
|
+
|
|
5
|
+
A minimal **MCP server** that helps bootstrap an “agentic-ready” repository by **creating a small set of standard files** (e.g., `.github/copilot-instructions.md`, prompts, agents, `AGENTS.md`). The server provides **static, high-quality starter templates** and (optionally) supports **prompt-driven generation** via the client’s LLM (e.g., VS Code Copilot). The goal is to ship quickly with a narrow scope.
|
|
6
|
+
|
|
7
|
+
## 2) Goals
|
|
8
|
+
|
|
9
|
+
**MVP goals**
|
|
10
|
+
|
|
11
|
+
- Provide an MCP server usable from **VS Code** (Windows + macOS; Linux optional).
|
|
12
|
+
- Generate and/or write a baseline file set that makes a repo immediately usable with agentic workflows.
|
|
13
|
+
- Keep behavior **safe-by-default**: _plan first_, then apply changes with clear overwrite rules.
|
|
14
|
+
- Templates can be:
|
|
15
|
+
- **Static** (prefilled content shipped with the server), and/or
|
|
16
|
+
- **Prompt-driven** (server supplies prompts; the client LLM produces content).
|
|
17
|
+
|
|
18
|
+
**Non-goals (MVP)**
|
|
19
|
+
|
|
20
|
+
- No remote downloads / resource pack syncing
|
|
21
|
+
- No shell/git command execution “recipes”
|
|
22
|
+
- No PR creation or GitHub integration
|
|
23
|
+
- No deep stack detection or extensive customization packs
|
|
24
|
+
- No background automation
|
|
25
|
+
|
|
26
|
+
## 3) Target users & environment
|
|
27
|
+
|
|
28
|
+
- Users working in **VS Code** with an MCP-capable agent/chat client.
|
|
29
|
+
- Primary platforms: **Windows + macOS** (Linux optional).
|
|
30
|
+
- Works in any repo/stack (templates are generic and useful everywhere).
|
|
31
|
+
|
|
32
|
+
## 4) Product shape: MCP server capabilities
|
|
33
|
+
|
|
34
|
+
The MCP server exposes **Tools**, optional **Prompts**, and optional **Resources**:
|
|
35
|
+
|
|
36
|
+
### Tools (MVP)
|
|
37
|
+
|
|
38
|
+
1. `awb.plan_bootstrap`
|
|
39
|
+
|
|
40
|
+
- Purpose: compute a deterministic plan of file operations (create/update/skip).
|
|
41
|
+
- Output includes full file contents (static templates), plus warnings when files already exist.
|
|
42
|
+
|
|
43
|
+
2. `awb.apply_bootstrap`
|
|
44
|
+
|
|
45
|
+
- Purpose: apply a previously returned plan to disk (atomic writes, idempotent behavior).
|
|
46
|
+
- Server enforces overwrite policy; never blindly clobbers existing user content unless explicitly allowed.
|
|
47
|
+
|
|
48
|
+
_(Optional but still “small”)_ 3) `awb.detect_repo_hints`
|
|
49
|
+
|
|
50
|
+
- Purpose: very lightweight heuristics (presence of lockfiles, `package.json`, `pyproject.toml`, etc.) to fill small placeholders like “preferred package manager” in templates.
|
|
51
|
+
- Must not turn into full “stack detection framework.”
|
|
52
|
+
|
|
53
|
+
### Prompts (MVP, optional)
|
|
54
|
+
|
|
55
|
+
- `bootstrap_agentic_repo`: a prompt that instructs the client LLM to call `plan_bootstrap`, review output, then call `apply_bootstrap`.
|
|
56
|
+
- `draft_readme_patch`: optional prompt for the client LLM to propose a README patch based on existing README + repo hints.
|
|
57
|
+
|
|
58
|
+
### Resources (MVP, optional)
|
|
59
|
+
|
|
60
|
+
- Expose template files as MCP “resources” (read-only), e.g.:
|
|
61
|
+
- `awb://templates/copilot-instructions.md`
|
|
62
|
+
- `awb://templates/AGENTS.md`
|
|
63
|
+
This is optional if your tools already return templates.
|
|
64
|
+
|
|
65
|
+
## 5) Baseline scaffold (MVP)
|
|
66
|
+
|
|
67
|
+
The server supports generating the following tree:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
.github/
|
|
71
|
+
copilot-instructions.md
|
|
72
|
+
instructions/
|
|
73
|
+
general.instructions.md
|
|
74
|
+
testing.instructions.md
|
|
75
|
+
prompts/
|
|
76
|
+
readme.prompt.md
|
|
77
|
+
onboarding.prompt.md
|
|
78
|
+
change-summary.prompt.md
|
|
79
|
+
agents/
|
|
80
|
+
architect.agent.md
|
|
81
|
+
implementer.agent.md
|
|
82
|
+
reviewer.agent.md
|
|
83
|
+
AGENTS.md
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Template content strategy (MVP)
|
|
87
|
+
|
|
88
|
+
- Templates are **generic**, high-signal, and safe:
|
|
89
|
+
- emphasize “don’t guess,” “prefer repo scripts,” “write tests,” “avoid destructive commands”
|
|
90
|
+
|
|
91
|
+
- Keep placeholders minimal and optional (e.g., `{{packageManager}}`, `{{testCommand}}`) filled via `detect_repo_hints` or left as defaults.
|
|
92
|
+
|
|
93
|
+
## 6) Generation modes
|
|
94
|
+
|
|
95
|
+
### Mode A: Static templates (default)
|
|
96
|
+
|
|
97
|
+
- `plan_bootstrap` returns prefilled content for all files.
|
|
98
|
+
- Fast, deterministic, easiest to ship.
|
|
99
|
+
|
|
100
|
+
### Mode B: Prompt-driven content (optional)
|
|
101
|
+
|
|
102
|
+
- Server returns:
|
|
103
|
+
- either (a) prompts + minimal skeleton files, or
|
|
104
|
+
- (b) static templates that the client LLM is encouraged to refine before applying.
|
|
105
|
+
|
|
106
|
+
- **Important:** In MVP, the MCP server itself does **not** call third-party LLM APIs.
|
|
107
|
+
- Any “LLM generation” happens in the **client** (e.g., Copilot), using the user’s configured model/provider.
|
|
108
|
+
|
|
109
|
+
## 7) README handling (narrow MVP)
|
|
110
|
+
|
|
111
|
+
- If `README.md` exists, the server **does not modify it** by default.
|
|
112
|
+
- Optional flag to include a README suggestion:
|
|
113
|
+
- `plan_bootstrap` can return a _separate_ proposed patch (or a new `README.agentic.md`) rather than overwriting `README.md`.
|
|
114
|
+
|
|
115
|
+
- The client (LLM/user) decides whether to apply README changes.
|
|
116
|
+
|
|
117
|
+
## 8) Overwrite & idempotency rules (MVP)
|
|
118
|
+
|
|
119
|
+
- If a target file **does not exist** → `CREATE`
|
|
120
|
+
- If it **exists** → default `SKIP`, unless:
|
|
121
|
+
- file contains an **AWB managed block** marker, or
|
|
122
|
+
- user passes `overwrite: true` for that specific file
|
|
123
|
+
|
|
124
|
+
## 9) Tool contracts (MVP)
|
|
125
|
+
|
|
126
|
+
### 9.1 `awb.plan_bootstrap`
|
|
127
|
+
|
|
128
|
+
**Input**
|
|
129
|
+
|
|
130
|
+
- `rootPath`: string (repo/workspace root)
|
|
131
|
+
- `options` (optional):
|
|
132
|
+
- `mode`: `"static"` | `"prompt"` (default `"static"`)
|
|
133
|
+
- `includeReadmeProposal`: boolean (default `false`)
|
|
134
|
+
- `overwritePolicy`: `"skip"` | `"managed-only"` | `"overwrite"` (default `"managed-only"`)
|
|
135
|
+
- `files`: optional allowlist of paths to generate (to keep scope minimal)
|
|
136
|
+
|
|
137
|
+
**Output**
|
|
138
|
+
|
|
139
|
+
- `planId`: string
|
|
140
|
+
- `operations[]`:
|
|
141
|
+
- `path`
|
|
142
|
+
- `action`: `CREATE` | `UPDATE_MANAGED` | `SKIP`
|
|
143
|
+
- `content` (when CREATE/UPDATE_MANAGED)
|
|
144
|
+
- `reason` (especially for SKIP)
|
|
145
|
+
- `warnings[]` (e.g., “file exists; not overwriting”)
|
|
146
|
+
|
|
147
|
+
- `repoHints` (optional): from detect logic
|
|
148
|
+
- `readmeProposal` (optional): separate content/patch suggestion
|
|
149
|
+
|
|
150
|
+
### 9.2 `awb.apply_bootstrap`
|
|
151
|
+
|
|
152
|
+
**Input**
|
|
153
|
+
|
|
154
|
+
- `rootPath`
|
|
155
|
+
- `planId`
|
|
156
|
+
- `operations[]` (echo back from plan; server can validate)
|
|
157
|
+
- `applyOptions` (optional):
|
|
158
|
+
- `dryRun`: boolean
|
|
159
|
+
- `writeMode`: `"atomic"` (default)
|
|
160
|
+
- `failOnConflict`: boolean (default true)
|
|
161
|
+
|
|
162
|
+
**Output**
|
|
163
|
+
|
|
164
|
+
- `results[]`:
|
|
165
|
+
- `path`
|
|
166
|
+
- `status`: `WRITTEN` | `SKIPPED` | `FAILED`
|
|
167
|
+
- `details`
|
|
168
|
+
|
|
169
|
+
## 10) Security & safety (MVP)
|
|
170
|
+
|
|
171
|
+
- No network calls required.
|
|
172
|
+
- No shell execution.
|
|
173
|
+
- No reading sensitive files beyond what’s needed to check existence and optionally read small config files for hints (e.g., `package.json`).
|
|
174
|
+
- Writes are atomic (temp file + rename).
|
|
175
|
+
- Clear warnings when files exist.
|
|
176
|
+
|
|
177
|
+
## 11) Distribution & deployment (ship-fast)
|
|
178
|
+
|
|
179
|
+
### Packaging recommendation (fastest)
|
|
180
|
+
|
|
181
|
+
- Implement in **Node + TypeScript** using the official MCP SDK.
|
|
182
|
+
- Publish to npm as: `awb-mcp` (working name)
|
|
183
|
+
- Provide a single executable entrypoint that starts the MCP server over stdio.
|
|
184
|
+
|
|
185
|
+
### Install / run
|
|
186
|
+
|
|
187
|
+
- `npm i -g awb-mcp`
|
|
188
|
+
- Configure VS Code MCP settings to launch `awb-mcp` as a server (stdio).
|
|
189
|
+
- Keep configuration minimal: just the command path.
|
|
190
|
+
|
|
191
|
+
### Versioning
|
|
192
|
+
|
|
193
|
+
- SemVer, but start `0.x` while iterating quickly.
|
|
194
|
+
|
|
195
|
+
## 12) Success metrics (MVP)
|
|
196
|
+
|
|
197
|
+
- Time-to-bootstrap: user can scaffold in < 1 minute.
|
|
198
|
+
- Idempotency: second run produces zero unexpected changes.
|
|
199
|
+
- Safety: no accidental overwrites; clear SKIP behavior.
|
|
200
|
+
|
|
201
|
+
## 13) MVP milestones (tight)
|
|
202
|
+
|
|
203
|
+
- **v0.1**
|
|
204
|
+
- MCP server runs in VS Code
|
|
205
|
+
- `plan_bootstrap` + `apply_bootstrap`
|
|
206
|
+
- static templates, managed-block support, atomic writes
|
|
207
|
+
|
|
208
|
+
- **v0.2**
|
|
209
|
+
- optional `detect_repo_hints` + placeholder fill
|
|
210
|
+
- optional README proposal (separate file or patch output)
|
|
211
|
+
|
|
212
|
+
- **v0.3 (only if needed)**
|
|
213
|
+
- “prompt mode” where server returns prompt(s) + skeletons (client LLM refines)
|
|
214
|
+
|
|
215
|
+
---
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
You are an implementation-focused coding agent working in this repo.
|
|
2
|
+
|
|
3
|
+
## Source of truth
|
|
4
|
+
|
|
5
|
+
Use `@.ai/backlog.md` as the authoritative spec. Do not re-ask for requirements that are already in the story.
|
|
6
|
+
|
|
7
|
+
## Task
|
|
8
|
+
|
|
9
|
+
Implement Story [N] from `@.ai/backlog.md`. Use subagent to gather info about user story.
|
|
10
|
+
|
|
11
|
+
## Operating rules
|
|
12
|
+
|
|
13
|
+
- First, open `@.ai/backlog.md`, locate Story [N], and copy its Acceptance Criteria into a checklist.
|
|
14
|
+
- Treat “Edge Cases”, “Technical Considerations”, “Design Notes”, and “Feature context” as hard constraints.
|
|
15
|
+
- Safe by default. Avoid destructive actions unless the story explicitly requires them.
|
|
16
|
+
- Don’t guess: inspect existing code/scripts/config before choosing an approach. If something is unclear, state assumptions explicitly and proceed with the most conservative option.
|
|
17
|
+
|
|
18
|
+
- Find the correct integration points in the current codebase (minimize new architecture).
|
|
19
|
+
- Implement the smallest end-to-end change that satisfies all Acceptance Criteria and Edge Cases.
|
|
20
|
+
- Add/update tests as appropriate; run relevant build/lint/test commands.
|
|
21
|
+
|
|
22
|
+
## Done means
|
|
23
|
+
|
|
24
|
+
- Provide a final summary mapping: each Acceptance Criterion → where/how it’s satisfied.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import z from "zod";
|
|
7
|
+
// Process-level safety: avoid ungraceful exit on uncaught errors (e.g. malformed JSON)
|
|
8
|
+
// SDK transport already catches parse errors; these handlers are a last resort.
|
|
9
|
+
process.on("uncaughtException", (err) => {
|
|
10
|
+
console.error("[awb-mcp] uncaughtException:", err?.message ?? err);
|
|
11
|
+
process.exitCode = 1;
|
|
12
|
+
setTimeout(() => process.exit(1), 100);
|
|
13
|
+
});
|
|
14
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
15
|
+
console.error("[awb-mcp] unhandledRejection:", reason);
|
|
16
|
+
process.exitCode = 1;
|
|
17
|
+
setTimeout(() => process.exit(1), 100);
|
|
18
|
+
});
|
|
19
|
+
const server = new McpServer({
|
|
20
|
+
name: "awb-mcp",
|
|
21
|
+
version: "0.0.1",
|
|
22
|
+
description: "AWB MCP Server — bootstrap agentic-ready repos via MCP",
|
|
23
|
+
});
|
|
24
|
+
function validateRootPath(rootPath) {
|
|
25
|
+
if (rootPath === undefined || rootPath === null) {
|
|
26
|
+
return { ok: false, error: "rootPath is required and must be a non-empty string." };
|
|
27
|
+
}
|
|
28
|
+
if (typeof rootPath !== "string") {
|
|
29
|
+
return { ok: false, error: "rootPath must be a string." };
|
|
30
|
+
}
|
|
31
|
+
const trimmed = rootPath.trim();
|
|
32
|
+
if (trimmed === "") {
|
|
33
|
+
return { ok: false, error: "rootPath cannot be empty." };
|
|
34
|
+
}
|
|
35
|
+
let resolved;
|
|
36
|
+
try {
|
|
37
|
+
resolved = path.resolve(trimmed);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return { ok: false, error: `rootPath could not be resolved: ${trimmed}` };
|
|
41
|
+
}
|
|
42
|
+
if (!fs.existsSync(resolved)) {
|
|
43
|
+
return { ok: false, error: `rootPath does not exist: ${resolved}` };
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const stat = fs.statSync(resolved);
|
|
47
|
+
if (!stat.isDirectory()) {
|
|
48
|
+
return { ok: false, error: `rootPath is not a directory: ${resolved}` };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
53
|
+
return { ok: false, error: `rootPath could not be read: ${msg}` };
|
|
54
|
+
}
|
|
55
|
+
return { ok: true, resolved };
|
|
56
|
+
}
|
|
57
|
+
function errorContent(text) {
|
|
58
|
+
return { content: [{ type: "text", text }] };
|
|
59
|
+
}
|
|
60
|
+
const PlanBootstrapInputSchema = z.object({
|
|
61
|
+
rootPath: z.string().min(1, "rootPath is required"),
|
|
62
|
+
});
|
|
63
|
+
const ApplyBootstrapInputSchema = z.object({
|
|
64
|
+
rootPath: z.string().min(1, "rootPath is required"),
|
|
65
|
+
planId: z.string(),
|
|
66
|
+
operations: z.array(z.unknown()).optional(),
|
|
67
|
+
});
|
|
68
|
+
server.registerTool("awb.plan_bootstrap", {
|
|
69
|
+
title: "Plan Bootstrap",
|
|
70
|
+
description: "Compute a deterministic plan of file operations (create/update/skip) for bootstrapping an agentic-ready repo. Full implementation in Story 2.",
|
|
71
|
+
inputSchema: PlanBootstrapInputSchema,
|
|
72
|
+
annotations: {
|
|
73
|
+
readOnlyHint: true,
|
|
74
|
+
destructiveHint: false,
|
|
75
|
+
idempotentHint: true,
|
|
76
|
+
openWorldHint: false,
|
|
77
|
+
},
|
|
78
|
+
}, async (args) => {
|
|
79
|
+
const parsed = PlanBootstrapInputSchema.safeParse(args);
|
|
80
|
+
if (!parsed.success) {
|
|
81
|
+
return errorContent(`Invalid input: ${parsed.error.message}`);
|
|
82
|
+
}
|
|
83
|
+
const { rootPath } = parsed.data;
|
|
84
|
+
const validation = validateRootPath(rootPath);
|
|
85
|
+
if (!validation.ok) {
|
|
86
|
+
return errorContent(`Error: ${validation.error}`);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: "text",
|
|
92
|
+
text: `plan_bootstrap stub: rootPath validated (${validation.resolved}). Full plan generation is implemented in Story 2.`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
server.registerTool("awb.apply_bootstrap", {
|
|
98
|
+
title: "Apply Bootstrap",
|
|
99
|
+
description: "Apply a previously returned bootstrap plan to disk (atomic writes). Full implementation in Story 3.",
|
|
100
|
+
inputSchema: ApplyBootstrapInputSchema,
|
|
101
|
+
annotations: {
|
|
102
|
+
readOnlyHint: false,
|
|
103
|
+
destructiveHint: false,
|
|
104
|
+
idempotentHint: true,
|
|
105
|
+
openWorldHint: false,
|
|
106
|
+
},
|
|
107
|
+
}, async (args) => {
|
|
108
|
+
const parsed = ApplyBootstrapInputSchema.safeParse(args);
|
|
109
|
+
if (!parsed.success) {
|
|
110
|
+
return errorContent(`Invalid input: ${parsed.error.message}`);
|
|
111
|
+
}
|
|
112
|
+
const { rootPath } = parsed.data;
|
|
113
|
+
const validation = validateRootPath(rootPath);
|
|
114
|
+
if (!validation.ok) {
|
|
115
|
+
return errorContent(`Error: ${validation.error}`);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
content: [
|
|
119
|
+
{
|
|
120
|
+
type: "text",
|
|
121
|
+
text: `apply_bootstrap stub: rootPath validated (${validation.resolved}). No files written. Full apply is implemented in Story 3.`,
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
const transport = new StdioServerTransport();
|
|
127
|
+
await server.connect(transport);
|
|
128
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kamkom/awb-mcp",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "AWB MCP Server — bootstrap agentic-ready repos via MCP",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"bin": {
|
|
10
|
+
"awb-mcp": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=18"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"start": "npx tsx src/index.ts",
|
|
17
|
+
"dev": "tsx watch src/index.ts",
|
|
18
|
+
"build": "tsc"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"tsx": "^4.19.2",
|
|
22
|
+
"typescript": "^5.8.2",
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.6.1",
|
|
24
|
+
"zod": "^3.24.2"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"typescript": "^5.8.2",
|
|
28
|
+
"tsx": "^4.19.2",
|
|
29
|
+
"@types/node": "^22.13.10"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import z from "zod";
|
|
7
|
+
|
|
8
|
+
// Process-level safety: avoid ungraceful exit on uncaught errors (e.g. malformed JSON)
|
|
9
|
+
// SDK transport already catches parse errors; these handlers are a last resort.
|
|
10
|
+
process.on("uncaughtException", (err) => {
|
|
11
|
+
console.error("[awb-mcp] uncaughtException:", err?.message ?? err);
|
|
12
|
+
process.exitCode = 1;
|
|
13
|
+
setTimeout(() => process.exit(1), 100);
|
|
14
|
+
});
|
|
15
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
16
|
+
console.error("[awb-mcp] unhandledRejection:", reason);
|
|
17
|
+
process.exitCode = 1;
|
|
18
|
+
setTimeout(() => process.exit(1), 100);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const server = new McpServer({
|
|
22
|
+
name: "awb-mcp",
|
|
23
|
+
version: "0.0.1",
|
|
24
|
+
description: "AWB MCP Server — bootstrap agentic-ready repos via MCP",
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
function validateRootPath(rootPath: unknown): { ok: true; resolved: string } | { ok: false; error: string } {
|
|
28
|
+
if (rootPath === undefined || rootPath === null) {
|
|
29
|
+
return { ok: false, error: "rootPath is required and must be a non-empty string." };
|
|
30
|
+
}
|
|
31
|
+
if (typeof rootPath !== "string") {
|
|
32
|
+
return { ok: false, error: "rootPath must be a string." };
|
|
33
|
+
}
|
|
34
|
+
const trimmed = rootPath.trim();
|
|
35
|
+
if (trimmed === "") {
|
|
36
|
+
return { ok: false, error: "rootPath cannot be empty." };
|
|
37
|
+
}
|
|
38
|
+
let resolved: string;
|
|
39
|
+
try {
|
|
40
|
+
resolved = path.resolve(trimmed);
|
|
41
|
+
} catch {
|
|
42
|
+
return { ok: false, error: `rootPath could not be resolved: ${trimmed}` };
|
|
43
|
+
}
|
|
44
|
+
if (!fs.existsSync(resolved)) {
|
|
45
|
+
return { ok: false, error: `rootPath does not exist: ${resolved}` };
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
const stat = fs.statSync(resolved);
|
|
49
|
+
if (!stat.isDirectory()) {
|
|
50
|
+
return { ok: false, error: `rootPath is not a directory: ${resolved}` };
|
|
51
|
+
}
|
|
52
|
+
} catch (e) {
|
|
53
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
54
|
+
return { ok: false, error: `rootPath could not be read: ${msg}` };
|
|
55
|
+
}
|
|
56
|
+
return { ok: true, resolved };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function errorContent(text: string) {
|
|
60
|
+
return { content: [{ type: "text" as const, text }] };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const PlanBootstrapInputSchema = z.object({
|
|
64
|
+
rootPath: z.string().min(1, "rootPath is required"),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const ApplyBootstrapInputSchema = z.object({
|
|
68
|
+
rootPath: z.string().min(1, "rootPath is required"),
|
|
69
|
+
planId: z.string(),
|
|
70
|
+
operations: z.array(z.unknown()).optional(),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
server.registerTool(
|
|
74
|
+
"awb.plan_bootstrap",
|
|
75
|
+
{
|
|
76
|
+
title: "Plan Bootstrap",
|
|
77
|
+
description: "Compute a deterministic plan of file operations (create/update/skip) for bootstrapping an agentic-ready repo. Full implementation in Story 2.",
|
|
78
|
+
inputSchema: PlanBootstrapInputSchema,
|
|
79
|
+
annotations: {
|
|
80
|
+
readOnlyHint: true,
|
|
81
|
+
destructiveHint: false,
|
|
82
|
+
idempotentHint: true,
|
|
83
|
+
openWorldHint: false,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
async (args) => {
|
|
87
|
+
const parsed = PlanBootstrapInputSchema.safeParse(args);
|
|
88
|
+
if (!parsed.success) {
|
|
89
|
+
return errorContent(`Invalid input: ${parsed.error.message}`);
|
|
90
|
+
}
|
|
91
|
+
const { rootPath } = parsed.data;
|
|
92
|
+
const validation = validateRootPath(rootPath);
|
|
93
|
+
if (!validation.ok) {
|
|
94
|
+
return errorContent(`Error: ${validation.error}`);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: "text" as const,
|
|
100
|
+
text: `plan_bootstrap stub: rootPath validated (${validation.resolved}). Full plan generation is implemented in Story 2.`,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
server.registerTool(
|
|
108
|
+
"awb.apply_bootstrap",
|
|
109
|
+
{
|
|
110
|
+
title: "Apply Bootstrap",
|
|
111
|
+
description: "Apply a previously returned bootstrap plan to disk (atomic writes). Full implementation in Story 3.",
|
|
112
|
+
inputSchema: ApplyBootstrapInputSchema,
|
|
113
|
+
annotations: {
|
|
114
|
+
readOnlyHint: false,
|
|
115
|
+
destructiveHint: false,
|
|
116
|
+
idempotentHint: true,
|
|
117
|
+
openWorldHint: false,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
async (args) => {
|
|
121
|
+
const parsed = ApplyBootstrapInputSchema.safeParse(args);
|
|
122
|
+
if (!parsed.success) {
|
|
123
|
+
return errorContent(`Invalid input: ${parsed.error.message}`);
|
|
124
|
+
}
|
|
125
|
+
const { rootPath } = parsed.data;
|
|
126
|
+
const validation = validateRootPath(rootPath);
|
|
127
|
+
if (!validation.ok) {
|
|
128
|
+
return errorContent(`Error: ${validation.error}`);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
content: [
|
|
132
|
+
{
|
|
133
|
+
type: "text" as const,
|
|
134
|
+
text: `apply_bootstrap stub: rootPath validated (${validation.resolved}). No files written. Full apply is implemented in Story 3.`,
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const transport = new StdioServerTransport();
|
|
142
|
+
await server.connect(transport);
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "nodenext",
|
|
5
|
+
"moduleResolution": "nodenext",
|
|
6
|
+
"rootDir": "src",
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"declarationMap": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
|
14
|
+
"skipLibCheck": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src"]
|
|
17
|
+
}
|