@tac0de/project-bootstrap-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 wonyoung choi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # Project Bootstrap MCP — PRD
2
+
3
+ This project turns the MCP (Management Control Plane) bootstrap documentation into runnable tooling: an Express-based API plus CLI that teams can publish via GitHub and npm.
4
+
5
+ ## 1. Project Goal
6
+
7
+ - **What is this project?**
8
+ An API server and CLI that surface PRD/TRD definitions, agent rules, and related operations so MCP bootstraps can be executed programmatically.
9
+ - **What problem does it solve?**
10
+ Markdown-only documentation cannot be executed; this project exposes those definitions through a lightweight service and CLI, making MCP setup automatable.
11
+ - **Who is the primary user?**
12
+ Platform engineers or operation teams bootstrapping MCPs, and any development team that installs this package via npm.
13
+
14
+ ## 2. Success Definition
15
+
16
+ - **How do we know this project is successful?**
17
+ By installing the npm package and starting the CLI/server, users can POST/GET bootstrap definitions through the provided REST endpoints.
18
+ - **What measurable outcome must be achieved?**
19
+ `npm test` passes, the Express API listens on port 4000 (default), and at least one bootstrap definition can be created and retrieved.
20
+ - **What does failure look like?**
21
+ The repository still only contains Markdown documentation or lacks automated verification for the API/CLI behavior.
22
+
23
+ ## 3. Core User Action
24
+
25
+ - **What is the single most important action a user performs?**
26
+ Run `project-bootstrap-mcp` (or `npm start`) to launch the server and interact with `/api/bootstraps`.
27
+ - **What moment should make the user say “this works”?**
28
+ A successful `POST /api/bootstraps` followed immediately by a `GET /api/bootstraps/:id` that returns the stored definition.
29
+
30
+ ## 4. Non-Goals
31
+
32
+ - **What will this project NOT attempt to do?**
33
+ It does not introduce persistent storage, authentication/authorization, multi-tenancy, or automated agent scheduling.
34
+ - **What features are explicitly out of scope?**
35
+ Connecting agents to external workflows or configuring high-availability deployments is not part of the MVP.
36
+
37
+ ## 5. Constraints
38
+
39
+ - **Time constraints:** Deliver the MVP API, CLI, and documentation within 1–2 days.
40
+ - **Scope constraints:** Single Node/Express service, simple CLI, and PRD/TRD CRUD operations only.
41
+ - **Resource constraints:** No external databases; use in-memory storage and only trusted, minimal dependencies (Express and built-in Node modules).
42
+
43
+ ## Validation Check
44
+
45
+ - **Can the project goal be explained in one sentence?** Yes — “Expose MCP bootstrap docs as runnable API/CLI tooling ready for npm/GitHub distribution.”
46
+ - **Can success be evaluated without subjective judgment?** Yes — API functionality and `npm test` results provide objective verification.
47
+
48
+ ## Implementation & Usage
49
+
50
+ ### API Overview
51
+
52
+ - `GET /api/status`: Returns service metadata (name, version), uptime, and timestamp.
53
+ - `GET /api/bootstraps`: Lists all stored bootstrap plans.
54
+ - `POST /api/bootstraps`: Validates and stores a bootstrap definition with `name`, `description`, `primaryAction`, and `steps`.
55
+ - `GET /api/bootstraps/:id`: Retrieves a bootstrap plan by ID.
56
+
57
+ ### CLI / Execution
58
+
59
+ - `npm start` or the `project-bootstrap-mcp` binary starts the server on port 4000 by default.
60
+ - Override the port with `--port` or `-p` (e.g., `project-bootstrap-mcp --port 5000`), or set the `PORT` environment variable.
61
+
62
+ ### Testing & Validation
63
+
64
+ - Run `npm test` to execute `node --test test/store.test.js`, which verifies the in-memory store and payload validation helper.
package/TRD.md ADDED
@@ -0,0 +1,38 @@
1
+ # TRD — Project Bootstrap MCP
2
+
3
+ This document captures the technical boundaries that support the PRD; it intentionally lives alongside PRD documentation so the architecture, data, and operational expectations are transparent for the MCP project.
4
+
5
+ ## 1. Architecture Direction
6
+
7
+ - **Is this a single-user tool or multi-user system?**
8
+ A single-process tool targeting the platform or operations team that owns the MCP bootstrap; multi-tenant or concurrent-user support is explicitly deferred to later phases.
9
+ - **Is persistence required?**
10
+ Not for the MVP. In-memory storage suffices, but the `BootstrapStore` abstraction allows replacing the backing store with a file or database in the future.
11
+ - **Is real-time behavior required?**
12
+ No. RESTful request/response cycles (ms latency) are adequate; streaming or WebSocket behavior is out of scope for now.
13
+
14
+ ## 2. Data Model Boundaries
15
+
16
+ - **What data MUST be stored?**
17
+ Bootstrap plan metadata such as name, description, primary action, step list, and timestamps must be retained in memory for GET endpoints.
18
+ - **What data MUST NOT be stored?**
19
+ Authentication credentials, secret keys, or any sensitive tokens must never enter the store.
20
+ - **What data can be derived instead of persisted?**
21
+ Runtime metrics (e.g., total bootstrap count) are computed on demand rather than persistently stored.
22
+
23
+ ## 3. Technology Constraints
24
+
25
+ - **Required technologies:** Node.js 18+, Express, and the built-in `node:test` module for validation.
26
+ - **Forbidden technologies:** External databases, ORMs, authentication frameworks, and TypeScript (current focus is CommonJS).
27
+ - **Justification for constraints:** Keeping the stack minimal accelerates npm publishing and keeps the package lightweight for quick bootstraps.
28
+
29
+ ## 4. Operational Constraints
30
+
31
+ - **Expected scale:** Tens of concurrent requests at most; a single process serving on port 4000 (customizable via CLI flags).
32
+ - **Performance expectations:** Responses should stay in the millisecond range without caching or database overhead.
33
+ - **Security or compliance requirements:** Does not collect sensitive data, but CORS or authentication middleware can be layered on later if needed.
34
+
35
+ ## Validation Check
36
+
37
+ - **Do technical choices support the PRD goals?** Yes, the Express/CLI stack fulfills the API delivery and documentation-driven outcome defined in the PRD.
38
+ - **Are any technical decisions premature or unnecessary?** No, everything beyond in-memory storage or REST endpoints is deferred until future phases.
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ const startServer = require('../src/server');
3
+
4
+ function parsePort(args, envPort) {
5
+ let portValue = envPort || '4000';
6
+ args.forEach((arg, index, list) => {
7
+ if (arg === '--port' && list[index + 1]) {
8
+ portValue = list[index + 1];
9
+ }
10
+ if (arg.startsWith('--port=')) {
11
+ portValue = arg.split('=')[1];
12
+ }
13
+ if (arg === '-p' && list[index + 1]) {
14
+ portValue = list[index + 1];
15
+ }
16
+ });
17
+ const parsed = Number.parseInt(portValue, 10);
18
+ if (Number.isNaN(parsed)) {
19
+ console.error(`invalid port value: ${portValue}`);
20
+ process.exit(1);
21
+ }
22
+ return parsed;
23
+ }
24
+
25
+ const port = parsePort(process.argv.slice(2), process.env.PORT);
26
+ startServer({ port });
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@tac0de/project-bootstrap-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Project Bootstrap MCP API tooling for launching MCP bootstrap definitions and documentation.",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "project-bootstrap-mcp": "bin/project-bootstrap-mcp.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node ./bin/project-bootstrap-mcp.js",
11
+ "test": "node --test test/store.test.js"
12
+ },
13
+ "keywords": [
14
+ "mcp",
15
+ "bootstrap",
16
+ "api",
17
+ "cli",
18
+ "project-tools"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "author": "wonyoung choi",
24
+ "license": "MIT",
25
+ "type": "commonjs",
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "files": [
30
+ "bin",
31
+ "src",
32
+ "test",
33
+ "LICENSE",
34
+ "README.md",
35
+ "TRD.md"
36
+ ],
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/your-org/project-bootstrap-mcp.git"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/your-org/project-bootstrap-mcp/issues"
43
+ },
44
+ "homepage": "https://github.com/your-org/project-bootstrap-mcp#readme",
45
+ "dependencies": {
46
+ "express": "^4.19.2"
47
+ }
48
+ }
package/src/app.js ADDED
@@ -0,0 +1,71 @@
1
+ const express = require('express');
2
+ const path = require('path');
3
+ const { validateBootstrapPayload } = require('./validators');
4
+ const BootstrapStore = require('./store');
5
+
6
+ const pkg = require(path.join(__dirname, '..', 'package.json'));
7
+
8
+ function createApp(options = {}) {
9
+ const store = options.store || new BootstrapStore();
10
+ const app = express();
11
+
12
+ app.use(express.json());
13
+
14
+ app.get('/api/status', (req, res) => {
15
+ res.json({
16
+ service: pkg.name,
17
+ version: pkg.version,
18
+ uptime: process.uptime(),
19
+ timestamp: new Date().toISOString(),
20
+ });
21
+ });
22
+
23
+ app.get('/api/bootstraps', (req, res) => {
24
+ res.json({ items: store.list() });
25
+ });
26
+
27
+ app.post('/api/bootstraps', (req, res) => {
28
+ const validation = validateBootstrapPayload(req.body);
29
+ if (!validation.valid) {
30
+ return res.status(400).json({ errors: validation.errors });
31
+ }
32
+ const payload = {
33
+ name: req.body.name,
34
+ description: req.body.description,
35
+ primaryAction: req.body.primaryAction,
36
+ targetPlatforms: Array.isArray(req.body.targetPlatforms)
37
+ ? req.body.targetPlatforms
38
+ : [],
39
+ successCriteria: Array.isArray(req.body.successCriteria)
40
+ ? req.body.successCriteria
41
+ : [],
42
+ steps: req.body.steps,
43
+ };
44
+ const saved = store.add(payload);
45
+ res.status(201).json(saved);
46
+ });
47
+
48
+ app.get('/api/bootstraps/:id', (req, res) => {
49
+ const record = store.find(req.params.id);
50
+ if (!record) {
51
+ return res.status(404).json({ error: 'bootstrap not found' });
52
+ }
53
+ res.json(record);
54
+ });
55
+
56
+ app.use((req, res) => {
57
+ res.status(404).json({ error: 'not found' });
58
+ });
59
+
60
+ app.use((err, req, res, next) => {
61
+ /* eslint-disable no-console */
62
+ console.error('internal error', err);
63
+ /* eslint-enable no-console */
64
+ res.status(500).json({ error: 'internal server error' });
65
+ next(err);
66
+ });
67
+
68
+ return { app, store };
69
+ }
70
+
71
+ module.exports = createApp;
package/src/index.js ADDED
@@ -0,0 +1,9 @@
1
+ const BootstrapStore = require('./store');
2
+ const createApp = require('./app');
3
+ const startServer = require('./server');
4
+
5
+ module.exports = {
6
+ BootstrapStore,
7
+ createApp,
8
+ startServer,
9
+ };
package/src/server.js ADDED
@@ -0,0 +1,29 @@
1
+ const createApp = require('./app');
2
+
3
+ function startServer({ port = process.env.PORT || 4000, onStart } = {}) {
4
+ const { app } = createApp();
5
+ const server = app.listen(port, () => {
6
+ const message = `Project Bootstrap MCP listening on port ${port}`;
7
+ if (typeof onStart === 'function') {
8
+ onStart(message, port);
9
+ } else {
10
+ // eslint-disable-next-line no-console
11
+ console.log(message);
12
+ }
13
+ });
14
+
15
+ const graceful = () => {
16
+ server.close(() => {
17
+ // eslint-disable-next-line no-console
18
+ console.log('Project Bootstrap MCP shutting down');
19
+ process.exit(0);
20
+ });
21
+ };
22
+
23
+ process.on('SIGINT', graceful);
24
+ process.on('SIGTERM', graceful);
25
+
26
+ return { app, server };
27
+ }
28
+
29
+ module.exports = startServer;
package/src/store.js ADDED
@@ -0,0 +1,27 @@
1
+ class BootstrapStore {
2
+ constructor() {
3
+ this.items = new Map();
4
+ this.counter = 1;
5
+ }
6
+
7
+ list() {
8
+ return Array.from(this.items.values());
9
+ }
10
+
11
+ add(payload) {
12
+ const id = String(this.counter++);
13
+ const record = {
14
+ id,
15
+ createdAt: new Date().toISOString(),
16
+ ...payload,
17
+ };
18
+ this.items.set(id, record);
19
+ return record;
20
+ }
21
+
22
+ find(id) {
23
+ return this.items.get(id) ?? null;
24
+ }
25
+ }
26
+
27
+ module.exports = BootstrapStore;
@@ -0,0 +1,34 @@
1
+ function validateBootstrapPayload(payload) {
2
+ const errors = [];
3
+ if (!payload || typeof payload !== 'object') {
4
+ errors.push('payload must be an object');
5
+ return { valid: false, errors };
6
+ }
7
+
8
+ const { name, description, primaryAction, steps } = payload;
9
+ if (!name || typeof name !== 'string') {
10
+ errors.push('name is required and must be a string');
11
+ }
12
+ if (!description || typeof description !== 'string') {
13
+ errors.push('description is required and must be a string');
14
+ }
15
+ if (!primaryAction || typeof primaryAction !== 'string') {
16
+ errors.push('primaryAction is required and must be a string');
17
+ }
18
+ if (!Array.isArray(steps) || steps.length === 0) {
19
+ errors.push('steps must be a non-empty array');
20
+ } else {
21
+ steps.forEach((step, index) => {
22
+ if (!step || typeof step.title !== 'string') {
23
+ errors.push(`steps[${index}].title is required`);
24
+ }
25
+ if (!step || typeof step.detail !== 'string') {
26
+ errors.push(`steps[${index}].detail is required`);
27
+ }
28
+ });
29
+ }
30
+
31
+ return { valid: errors.length === 0, errors };
32
+ }
33
+
34
+ module.exports = { validateBootstrapPayload };
@@ -0,0 +1,44 @@
1
+ const test = require('node:test');
2
+ const assert = require('node:assert');
3
+ const BootstrapStore = require('../src/store');
4
+ const { validateBootstrapPayload } = require('../src/validators');
5
+
6
+ test('BootstrapStore should add and list records', () => {
7
+ const store = new BootstrapStore();
8
+ const payload = {
9
+ name: 'bootstrap-a',
10
+ description: 'test',
11
+ primaryAction: 'deploy',
12
+ steps: [
13
+ { title: 'step1', detail: 'run' },
14
+ ],
15
+ };
16
+
17
+ const record = store.add(payload);
18
+ assert.strictEqual(record.name, payload.name);
19
+ assert.strictEqual(record.id, '1');
20
+ const items = store.list();
21
+ assert.strictEqual(items.length, 1);
22
+ assert.strictEqual(store.find('1'), record);
23
+ });
24
+
25
+ test('BootstrapStore returns null for missing id', () => {
26
+ const store = new BootstrapStore();
27
+ assert.strictEqual(store.find('missing'), null);
28
+ });
29
+
30
+ test('validateBootstrapPayload rejects invalid data', () => {
31
+ const result = validateBootstrapPayload({});
32
+ assert.strictEqual(result.valid, false);
33
+ assert.ok(result.errors.length >= 1);
34
+ });
35
+
36
+ test('validateBootstrapPayload accepts valid payload', () => {
37
+ const result = validateBootstrapPayload({
38
+ name: 'valid',
39
+ description: 'desc',
40
+ primaryAction: 'run',
41
+ steps: [{ title: 't', detail: 'd' }],
42
+ });
43
+ assert.strictEqual(result.valid, true);
44
+ });