@tac0de/project-bootstrap-mcp 1.1.1 → 1.2.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/README.md +1 -1
- package/bin/project-bootstrap-mcp.js +2 -0
- package/dist/bin/project-bootstrap-mcp.js +0 -0
- package/dist/index.d.ts +1 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +182 -5
- package/dist/package.json +3 -3
- package/package.json +5 -6
- package/src/index.ts +202 -3
- package/bin/project-bootstrap-mcp.ts +0 -26
- package/src/app.ts +0 -73
- package/src/server.ts +0 -44
- package/src/validators.ts +0 -43
package/README.md
CHANGED
|
File without changes
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,cAAc,MAAM,SAAS,CAAC;AAqMrC,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,10 +3,187 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.BootstrapStore = void 0;
|
|
7
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
8
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
9
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
7
10
|
const store_1 = __importDefault(require("./store"));
|
|
8
11
|
exports.BootstrapStore = store_1.default;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
// Create MCP server
|
|
13
|
+
const server = new index_js_1.Server({
|
|
14
|
+
name: '@tac0de/project-bootstrap-mcp',
|
|
15
|
+
version: '1.1.2',
|
|
16
|
+
}, {
|
|
17
|
+
capabilities: {
|
|
18
|
+
tools: {},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
const store = new store_1.default();
|
|
22
|
+
// List available tools
|
|
23
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
24
|
+
return {
|
|
25
|
+
tools: [
|
|
26
|
+
{
|
|
27
|
+
name: 'list_bootstraps',
|
|
28
|
+
description: 'List all bootstrap project definitions',
|
|
29
|
+
inputSchema: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'create_bootstrap',
|
|
36
|
+
description: 'Create a new bootstrap project definition with steps and criteria',
|
|
37
|
+
inputSchema: {
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
name: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
description: 'Name of the bootstrap project',
|
|
43
|
+
},
|
|
44
|
+
description: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'Description of the bootstrap project',
|
|
47
|
+
},
|
|
48
|
+
primaryAction: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: 'The primary action to be performed',
|
|
51
|
+
},
|
|
52
|
+
targetPlatforms: {
|
|
53
|
+
type: 'array',
|
|
54
|
+
items: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
},
|
|
57
|
+
description: 'Target platforms for this bootstrap',
|
|
58
|
+
},
|
|
59
|
+
successCriteria: {
|
|
60
|
+
type: 'array',
|
|
61
|
+
items: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
},
|
|
64
|
+
description: 'Criteria for successful completion',
|
|
65
|
+
},
|
|
66
|
+
steps: {
|
|
67
|
+
type: 'array',
|
|
68
|
+
items: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
title: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
description: 'Title of the step',
|
|
74
|
+
},
|
|
75
|
+
detail: {
|
|
76
|
+
type: 'string',
|
|
77
|
+
description: 'Detailed instructions for the step',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
required: ['title', 'detail'],
|
|
81
|
+
},
|
|
82
|
+
description: 'Steps to complete the bootstrap',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
required: ['name', 'description', 'primaryAction', 'steps'],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'get_bootstrap',
|
|
90
|
+
description: 'Get a specific bootstrap project by ID',
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
id: {
|
|
95
|
+
type: 'string',
|
|
96
|
+
description: 'The ID of the bootstrap project',
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
required: ['id'],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
// Handle tool execution
|
|
106
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
107
|
+
const { name, arguments: args } = request.params;
|
|
108
|
+
switch (name) {
|
|
109
|
+
case 'list_bootstraps': {
|
|
110
|
+
const items = store.list();
|
|
111
|
+
return {
|
|
112
|
+
content: [
|
|
113
|
+
{
|
|
114
|
+
type: 'text',
|
|
115
|
+
text: JSON.stringify({ items }, null, 2),
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
case 'create_bootstrap': {
|
|
121
|
+
const payload = {
|
|
122
|
+
name: String(args?.name),
|
|
123
|
+
description: String(args?.description),
|
|
124
|
+
primaryAction: String(args?.primaryAction),
|
|
125
|
+
targetPlatforms: Array.isArray(args?.targetPlatforms)
|
|
126
|
+
? args.targetPlatforms.map(String)
|
|
127
|
+
: [],
|
|
128
|
+
successCriteria: Array.isArray(args?.successCriteria)
|
|
129
|
+
? args.successCriteria.map(String)
|
|
130
|
+
: [],
|
|
131
|
+
steps: Array.isArray(args?.steps)
|
|
132
|
+
? args.steps
|
|
133
|
+
: [],
|
|
134
|
+
};
|
|
135
|
+
const saved = store.add(payload);
|
|
136
|
+
return {
|
|
137
|
+
content: [
|
|
138
|
+
{
|
|
139
|
+
type: 'text',
|
|
140
|
+
text: JSON.stringify(saved, null, 2),
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
case 'get_bootstrap': {
|
|
146
|
+
const id = String(args?.id);
|
|
147
|
+
const record = store.find(id);
|
|
148
|
+
if (!record) {
|
|
149
|
+
return {
|
|
150
|
+
content: [
|
|
151
|
+
{
|
|
152
|
+
type: 'text',
|
|
153
|
+
text: JSON.stringify({ error: 'Bootstrap not found' }, null, 2),
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
isError: true,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: 'text',
|
|
163
|
+
text: JSON.stringify(record, null, 2),
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
default:
|
|
169
|
+
return {
|
|
170
|
+
content: [
|
|
171
|
+
{
|
|
172
|
+
type: 'text',
|
|
173
|
+
text: JSON.stringify({ error: 'Unknown tool' }, null, 2),
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
isError: true,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
// Start the server
|
|
181
|
+
async function main() {
|
|
182
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
183
|
+
await server.connect(transport);
|
|
184
|
+
console.error('Project Bootstrap MCP server running on stdio');
|
|
185
|
+
}
|
|
186
|
+
main().catch((error) => {
|
|
187
|
+
console.error('Fatal error:', error);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
});
|
package/dist/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tac0de/project-bootstrap-mcp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Project Bootstrap MCP API tooling for launching MCP bootstrap definitions and documentation.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"project-bootstrap-mcp": "
|
|
7
|
+
"project-bootstrap-mcp": "bin/project-bootstrap-mcp.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
11
|
-
"start": "npm run build && node ./
|
|
11
|
+
"start": "npm run build && node ./bin/project-bootstrap-mcp.js",
|
|
12
12
|
"test": "node --test -r ts-node/register test/store.test.ts"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tac0de/project-bootstrap-mcp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "MCP server for managing project bootstrap definitions with steps and success criteria.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"project-bootstrap-mcp": "
|
|
7
|
+
"project-bootstrap-mcp": "bin/project-bootstrap-mcp.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
11
|
-
"start": "npm run build && node ./
|
|
11
|
+
"start": "npm run build && node ./bin/project-bootstrap-mcp.js",
|
|
12
12
|
"test": "node --test -r ts-node/register test/store.test.ts"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [
|
|
@@ -42,10 +42,9 @@
|
|
|
42
42
|
"url": "https://github.com/tac0de/project-bootstrap-mcp"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"
|
|
45
|
+
"@modelcontextprotocol/sdk": "^1.25.1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@types/express": "^5.0.6",
|
|
49
48
|
"@types/node": "^25.0.3",
|
|
50
49
|
"ts-node": "^10.9.2",
|
|
51
50
|
"typescript": "^5.9.3"
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,204 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import {
|
|
4
|
+
CallToolRequestSchema,
|
|
5
|
+
ListToolsRequestSchema,
|
|
6
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
1
7
|
import BootstrapStore from './store';
|
|
2
|
-
import
|
|
3
|
-
import startServer from './server';
|
|
8
|
+
import type { BootstrapPayload } from './types';
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
// Create MCP server
|
|
11
|
+
const server = new Server(
|
|
12
|
+
{
|
|
13
|
+
name: '@tac0de/project-bootstrap-mcp',
|
|
14
|
+
version: '1.1.2',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
capabilities: {
|
|
18
|
+
tools: {},
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const store = new BootstrapStore();
|
|
24
|
+
|
|
25
|
+
// List available tools
|
|
26
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
27
|
+
return {
|
|
28
|
+
tools: [
|
|
29
|
+
{
|
|
30
|
+
name: 'list_bootstraps',
|
|
31
|
+
description: 'List all bootstrap project definitions',
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'create_bootstrap',
|
|
39
|
+
description: 'Create a new bootstrap project definition with steps and criteria',
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
name: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'Name of the bootstrap project',
|
|
46
|
+
},
|
|
47
|
+
description: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: 'Description of the bootstrap project',
|
|
50
|
+
},
|
|
51
|
+
primaryAction: {
|
|
52
|
+
type: 'string',
|
|
53
|
+
description: 'The primary action to be performed',
|
|
54
|
+
},
|
|
55
|
+
targetPlatforms: {
|
|
56
|
+
type: 'array',
|
|
57
|
+
items: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
},
|
|
60
|
+
description: 'Target platforms for this bootstrap',
|
|
61
|
+
},
|
|
62
|
+
successCriteria: {
|
|
63
|
+
type: 'array',
|
|
64
|
+
items: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
},
|
|
67
|
+
description: 'Criteria for successful completion',
|
|
68
|
+
},
|
|
69
|
+
steps: {
|
|
70
|
+
type: 'array',
|
|
71
|
+
items: {
|
|
72
|
+
type: 'object',
|
|
73
|
+
properties: {
|
|
74
|
+
title: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
description: 'Title of the step',
|
|
77
|
+
},
|
|
78
|
+
detail: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
description: 'Detailed instructions for the step',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
required: ['title', 'detail'],
|
|
84
|
+
},
|
|
85
|
+
description: 'Steps to complete the bootstrap',
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
required: ['name', 'description', 'primaryAction', 'steps'],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'get_bootstrap',
|
|
93
|
+
description: 'Get a specific bootstrap project by ID',
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
id: {
|
|
98
|
+
type: 'string',
|
|
99
|
+
description: 'The ID of the bootstrap project',
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
required: ['id'],
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Handle tool execution
|
|
110
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
111
|
+
const { name, arguments: args } = request.params;
|
|
112
|
+
|
|
113
|
+
switch (name) {
|
|
114
|
+
case 'list_bootstraps': {
|
|
115
|
+
const items = store.list();
|
|
116
|
+
return {
|
|
117
|
+
content: [
|
|
118
|
+
{
|
|
119
|
+
type: 'text',
|
|
120
|
+
text: JSON.stringify({ items }, null, 2),
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
case 'create_bootstrap': {
|
|
127
|
+
const payload: BootstrapPayload = {
|
|
128
|
+
name: String(args?.name),
|
|
129
|
+
description: String(args?.description),
|
|
130
|
+
primaryAction: String(args?.primaryAction),
|
|
131
|
+
targetPlatforms: Array.isArray(args?.targetPlatforms)
|
|
132
|
+
? args.targetPlatforms.map(String)
|
|
133
|
+
: [],
|
|
134
|
+
successCriteria: Array.isArray(args?.successCriteria)
|
|
135
|
+
? args.successCriteria.map(String)
|
|
136
|
+
: [],
|
|
137
|
+
steps: Array.isArray(args?.steps)
|
|
138
|
+
? (args.steps as BootstrapPayload['steps'])
|
|
139
|
+
: [],
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const saved = store.add(payload);
|
|
143
|
+
return {
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: 'text',
|
|
147
|
+
text: JSON.stringify(saved, null, 2),
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
case 'get_bootstrap': {
|
|
154
|
+
const id = String(args?.id);
|
|
155
|
+
const record = store.find(id);
|
|
156
|
+
|
|
157
|
+
if (!record) {
|
|
158
|
+
return {
|
|
159
|
+
content: [
|
|
160
|
+
{
|
|
161
|
+
type: 'text',
|
|
162
|
+
text: JSON.stringify({ error: 'Bootstrap not found' }, null, 2),
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
isError: true,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
content: [
|
|
171
|
+
{
|
|
172
|
+
type: 'text',
|
|
173
|
+
text: JSON.stringify(record, null, 2),
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
default:
|
|
180
|
+
return {
|
|
181
|
+
content: [
|
|
182
|
+
{
|
|
183
|
+
type: 'text',
|
|
184
|
+
text: JSON.stringify({ error: 'Unknown tool' }, null, 2),
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
isError: true,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Start the server
|
|
193
|
+
async function main() {
|
|
194
|
+
const transport = new StdioServerTransport();
|
|
195
|
+
await server.connect(transport);
|
|
196
|
+
console.error('Project Bootstrap MCP server running on stdio');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
main().catch((error) => {
|
|
200
|
+
console.error('Fatal error:', error);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
export { BootstrapStore };
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import startServer from '../src/server.js';
|
|
3
|
-
|
|
4
|
-
function parsePort(args: string[], envPort: string | undefined): number {
|
|
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/src/app.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import express, { Express, Request, Response, NextFunction } from 'express';
|
|
2
|
-
import pkg from '../package.json';
|
|
3
|
-
import { validateBootstrapPayload } from './validators';
|
|
4
|
-
import BootstrapStore from './store';
|
|
5
|
-
import type { BootstrapPayload } from './types';
|
|
6
|
-
|
|
7
|
-
interface CreateAppOptions {
|
|
8
|
-
store?: BootstrapStore;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export default function createApp(options: CreateAppOptions = {}) {
|
|
12
|
-
const store = options.store ?? new BootstrapStore();
|
|
13
|
-
const app: Express = express();
|
|
14
|
-
|
|
15
|
-
app.use(express.json());
|
|
16
|
-
|
|
17
|
-
app.get('/api/status', (_req: Request, res: Response) => {
|
|
18
|
-
res.json({
|
|
19
|
-
service: pkg.name,
|
|
20
|
-
version: pkg.version,
|
|
21
|
-
uptime: process.uptime(),
|
|
22
|
-
timestamp: new Date().toISOString(),
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
app.get('/api/bootstraps', (_req: Request, res: Response) => {
|
|
27
|
-
res.json({ items: store.list() });
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
app.post('/api/bootstraps', (req: Request, res: Response) => {
|
|
31
|
-
const validation = validateBootstrapPayload(req.body);
|
|
32
|
-
if (!validation.valid) {
|
|
33
|
-
return res.status(400).json({ errors: validation.errors });
|
|
34
|
-
}
|
|
35
|
-
const payload: BootstrapPayload = {
|
|
36
|
-
name: req.body.name,
|
|
37
|
-
description: req.body.description,
|
|
38
|
-
primaryAction: req.body.primaryAction,
|
|
39
|
-
targetPlatforms: Array.isArray(req.body.targetPlatforms)
|
|
40
|
-
? req.body.targetPlatforms.map(String)
|
|
41
|
-
: [],
|
|
42
|
-
successCriteria: Array.isArray(req.body.successCriteria)
|
|
43
|
-
? req.body.successCriteria.map(String)
|
|
44
|
-
: [],
|
|
45
|
-
steps: Array.isArray(req.body.steps)
|
|
46
|
-
? (req.body.steps as BootstrapPayload['steps'])
|
|
47
|
-
: [],
|
|
48
|
-
};
|
|
49
|
-
const saved = store.add(payload);
|
|
50
|
-
res.status(201).json(saved);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
app.get('/api/bootstraps/:id', (req: Request, res: Response) => {
|
|
54
|
-
const record = store.find(req.params.id);
|
|
55
|
-
if (!record) {
|
|
56
|
-
return res.status(404).json({ error: 'bootstrap not found' });
|
|
57
|
-
}
|
|
58
|
-
res.json(record);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
app.use((_req: Request, res: Response) => {
|
|
62
|
-
res.status(404).json({ error: 'not found' });
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
app.use((err: Error, _req: Request, res: Response, next: NextFunction) => {
|
|
66
|
-
// eslint-disable-next-line no-console
|
|
67
|
-
console.error('internal error', err);
|
|
68
|
-
res.status(500).json({ error: 'internal server error' });
|
|
69
|
-
next(err);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
return { app, store };
|
|
73
|
-
}
|
package/src/server.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { Express } from 'express';
|
|
2
|
-
import type { Server } from 'http';
|
|
3
|
-
import createApp from './app';
|
|
4
|
-
|
|
5
|
-
export interface StartServerOptions {
|
|
6
|
-
port?: number;
|
|
7
|
-
onStart?: (message: string, port: number) => void;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface StartServerResult {
|
|
11
|
-
app: Express;
|
|
12
|
-
server: Server;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default function startServer({
|
|
16
|
-
port,
|
|
17
|
-
onStart,
|
|
18
|
-
}: StartServerOptions = {}): StartServerResult {
|
|
19
|
-
const defaultPort = Number(process.env.PORT) || 4000;
|
|
20
|
-
const resolvedPort = port ?? defaultPort;
|
|
21
|
-
const { app } = createApp();
|
|
22
|
-
const server = app.listen(resolvedPort, () => {
|
|
23
|
-
const message = `Project Bootstrap MCP listening on port ${resolvedPort}`;
|
|
24
|
-
if (typeof onStart === 'function') {
|
|
25
|
-
onStart(message, resolvedPort);
|
|
26
|
-
} else {
|
|
27
|
-
// eslint-disable-next-line no-console
|
|
28
|
-
console.log(message);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const graceful = () => {
|
|
33
|
-
server.close(() => {
|
|
34
|
-
// eslint-disable-next-line no-console
|
|
35
|
-
console.log('Project Bootstrap MCP shutting down');
|
|
36
|
-
process.exit(0);
|
|
37
|
-
});
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
process.on('SIGINT', graceful);
|
|
41
|
-
process.on('SIGTERM', graceful);
|
|
42
|
-
|
|
43
|
-
return { app, server };
|
|
44
|
-
}
|
package/src/validators.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { BootstrapPayload, BootstrapStep } from './types';
|
|
2
|
-
|
|
3
|
-
export interface ValidationResult {
|
|
4
|
-
valid: boolean;
|
|
5
|
-
errors: string[];
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function validateBootstrapPayload(payload: unknown): ValidationResult {
|
|
9
|
-
const errors: string[] = [];
|
|
10
|
-
if (!payload || typeof payload !== 'object') {
|
|
11
|
-
errors.push('payload must be an object');
|
|
12
|
-
return { valid: false, errors };
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const typedPayload = payload as BootstrapPayload;
|
|
16
|
-
const { name, description, primaryAction, steps } = typedPayload;
|
|
17
|
-
|
|
18
|
-
if (!name || typeof name !== 'string') {
|
|
19
|
-
errors.push('name is required and must be a string');
|
|
20
|
-
}
|
|
21
|
-
if (!description || typeof description !== 'string') {
|
|
22
|
-
errors.push('description is required and must be a string');
|
|
23
|
-
}
|
|
24
|
-
if (!primaryAction || typeof primaryAction !== 'string') {
|
|
25
|
-
errors.push('primaryAction is required and must be a string');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!Array.isArray(steps) || steps.length === 0) {
|
|
29
|
-
errors.push('steps must be a non-empty array');
|
|
30
|
-
} else {
|
|
31
|
-
steps.forEach((step, index) => {
|
|
32
|
-
const currentStep = step as BootstrapStep | undefined;
|
|
33
|
-
if (!currentStep || typeof currentStep.title !== 'string') {
|
|
34
|
-
errors.push(`steps[${index}].title is required`);
|
|
35
|
-
}
|
|
36
|
-
if (!currentStep || typeof currentStep.detail !== 'string') {
|
|
37
|
-
errors.push(`steps[${index}].detail is required`);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return { valid: errors.length === 0, errors };
|
|
43
|
-
}
|