framework-mcp 1.1.3 → 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/.do/app.yaml +78 -0
- package/DEPLOYMENT_GUIDE.md +335 -0
- package/RELEASE_NOTES_v1.2.0.md +396 -0
- package/dist/core/capability-analyzer.d.ts +29 -0
- package/dist/core/capability-analyzer.d.ts.map +1 -0
- package/dist/core/capability-analyzer.js +568 -0
- package/dist/core/capability-analyzer.js.map +1 -0
- package/dist/core/safeguard-manager.d.ts +15 -0
- package/dist/core/safeguard-manager.d.ts.map +1 -0
- package/dist/core/safeguard-manager.js +315 -0
- package/dist/core/safeguard-manager.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces/http/http-server.d.ts +17 -0
- package/dist/interfaces/http/http-server.d.ts.map +1 -0
- package/dist/interfaces/http/http-server.js +285 -0
- package/dist/interfaces/http/http-server.js.map +1 -0
- package/dist/interfaces/mcp/mcp-server.d.ts +18 -0
- package/dist/interfaces/mcp/mcp-server.d.ts.map +1 -0
- package/dist/interfaces/mcp/mcp-server.js +299 -0
- package/dist/interfaces/mcp/mcp-server.js.map +1 -0
- package/dist/shared/types.d.ts +89 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +3 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +16 -6
- package/src/core/capability-analyzer.ts +708 -0
- package/src/core/safeguard-manager.ts +339 -0
- package/src/index.ts +17 -0
- package/src/interfaces/http/http-server.ts +360 -0
- package/src/interfaces/mcp/mcp-server.ts +367 -0
- package/src/shared/types.ts +104 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import express from 'express';
|
|
4
|
+
import cors from 'cors';
|
|
5
|
+
import helmet from 'helmet';
|
|
6
|
+
import compression from 'compression';
|
|
7
|
+
import { CapabilityAnalyzer } from '../../core/capability-analyzer.js';
|
|
8
|
+
import { SafeguardManager } from '../../core/safeguard-manager.js';
|
|
9
|
+
|
|
10
|
+
interface ErrorResponse {
|
|
11
|
+
error: string;
|
|
12
|
+
details?: string;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class FrameworkHttpServer {
|
|
17
|
+
private app: express.Application;
|
|
18
|
+
private capabilityAnalyzer: CapabilityAnalyzer;
|
|
19
|
+
private safeguardManager: SafeguardManager;
|
|
20
|
+
private port: number;
|
|
21
|
+
|
|
22
|
+
constructor(port: number = 8080) {
|
|
23
|
+
this.app = express();
|
|
24
|
+
this.capabilityAnalyzer = new CapabilityAnalyzer();
|
|
25
|
+
this.safeguardManager = new SafeguardManager();
|
|
26
|
+
this.port = port;
|
|
27
|
+
|
|
28
|
+
this.setupMiddleware();
|
|
29
|
+
this.setupRoutes();
|
|
30
|
+
this.setupErrorHandling();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private setupMiddleware(): void {
|
|
34
|
+
// Security middleware
|
|
35
|
+
this.app.use(helmet({
|
|
36
|
+
contentSecurityPolicy: {
|
|
37
|
+
directives: {
|
|
38
|
+
defaultSrc: ["'self'"],
|
|
39
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
40
|
+
scriptSrc: ["'self'"],
|
|
41
|
+
imgSrc: ["'self'", "data:", "https:"],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
// CORS configuration
|
|
47
|
+
this.app.use(cors({
|
|
48
|
+
origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'],
|
|
49
|
+
methods: ['GET', 'POST', 'OPTIONS'],
|
|
50
|
+
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
|
|
51
|
+
credentials: true
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
// Compression and parsing
|
|
55
|
+
this.app.use(compression());
|
|
56
|
+
this.app.use(express.json({ limit: '10mb' }));
|
|
57
|
+
this.app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
58
|
+
|
|
59
|
+
// Request logging in production
|
|
60
|
+
if (process.env.NODE_ENV === 'production') {
|
|
61
|
+
this.app.use((req, res, next) => {
|
|
62
|
+
console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
|
|
63
|
+
next();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private setupRoutes(): void {
|
|
69
|
+
// Health check endpoint (required for DigitalOcean App Services)
|
|
70
|
+
this.app.get('/health', (req, res) => {
|
|
71
|
+
const metrics = this.capabilityAnalyzer.getPerformanceMetrics();
|
|
72
|
+
res.json({
|
|
73
|
+
status: 'healthy',
|
|
74
|
+
uptime: Math.round((Date.now() - metrics.uptime) / 1000),
|
|
75
|
+
totalRequests: metrics.totalRequests,
|
|
76
|
+
errorCount: metrics.errorCount,
|
|
77
|
+
version: '1.2.0',
|
|
78
|
+
timestamp: new Date().toISOString()
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Primary validation endpoint
|
|
83
|
+
this.app.post('/api/validate-vendor-mapping', async (req, res) => {
|
|
84
|
+
try {
|
|
85
|
+
const { vendor_name, safeguard_id, claimed_capability, supporting_text } = req.body;
|
|
86
|
+
|
|
87
|
+
// Input validation
|
|
88
|
+
this.validateInput(req.body, [
|
|
89
|
+
'vendor_name',
|
|
90
|
+
'safeguard_id',
|
|
91
|
+
'claimed_capability',
|
|
92
|
+
'supporting_text'
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
this.validateTextInput(supporting_text, 'Supporting text');
|
|
96
|
+
this.validateCapability(claimed_capability);
|
|
97
|
+
this.safeguardManager.validateSafeguardId(safeguard_id);
|
|
98
|
+
|
|
99
|
+
const safeguard = this.safeguardManager.getSafeguardDetails(safeguard_id);
|
|
100
|
+
if (!safeguard) {
|
|
101
|
+
return res.status(404).json(this.createErrorResponse('Safeguard not found'));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const result = this.capabilityAnalyzer.validateVendorMapping(
|
|
105
|
+
vendor_name,
|
|
106
|
+
safeguard_id,
|
|
107
|
+
claimed_capability,
|
|
108
|
+
supporting_text,
|
|
109
|
+
safeguard
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
res.json(result);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error('[HTTP Server] validate-vendor-mapping error:', error);
|
|
115
|
+
res.status(400).json(this.createErrorResponse(error instanceof Error ? error.message : 'Unknown error'));
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Capability analysis endpoint
|
|
120
|
+
this.app.post('/api/analyze-vendor-response', async (req, res) => {
|
|
121
|
+
try {
|
|
122
|
+
const { vendor_name, safeguard_id, response_text } = req.body;
|
|
123
|
+
|
|
124
|
+
this.validateInput(req.body, ['vendor_name', 'safeguard_id', 'response_text']);
|
|
125
|
+
this.validateTextInput(response_text, 'Response text');
|
|
126
|
+
this.safeguardManager.validateSafeguardId(safeguard_id);
|
|
127
|
+
|
|
128
|
+
const safeguard = this.safeguardManager.getSafeguardDetails(safeguard_id);
|
|
129
|
+
if (!safeguard) {
|
|
130
|
+
return res.status(404).json(this.createErrorResponse('Safeguard not found'));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const result = this.capabilityAnalyzer.performCapabilityAnalysis(
|
|
134
|
+
vendor_name,
|
|
135
|
+
safeguard,
|
|
136
|
+
response_text
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
res.json(result);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error('[HTTP Server] analyze-vendor-response error:', error);
|
|
142
|
+
res.status(400).json(this.createErrorResponse(error instanceof Error ? error.message : 'Unknown error'));
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Coverage claim validation endpoint
|
|
147
|
+
this.app.post('/api/validate-coverage-claim', async (req, res) => {
|
|
148
|
+
try {
|
|
149
|
+
const { vendor_name, safeguard_id, claimed_capability, response_text } = req.body;
|
|
150
|
+
|
|
151
|
+
this.validateInput(req.body, ['vendor_name', 'safeguard_id', 'claimed_capability', 'response_text']);
|
|
152
|
+
this.validateTextInput(response_text, 'Response text');
|
|
153
|
+
this.validateCapability(claimed_capability);
|
|
154
|
+
this.safeguardManager.validateSafeguardId(safeguard_id);
|
|
155
|
+
|
|
156
|
+
const safeguard = this.safeguardManager.getSafeguardDetails(safeguard_id);
|
|
157
|
+
if (!safeguard) {
|
|
158
|
+
return res.status(404).json(this.createErrorResponse('Safeguard not found'));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Use the primary validation method for coverage claims
|
|
162
|
+
const result = this.capabilityAnalyzer.validateVendorMapping(
|
|
163
|
+
vendor_name,
|
|
164
|
+
safeguard_id,
|
|
165
|
+
claimed_capability,
|
|
166
|
+
response_text,
|
|
167
|
+
safeguard
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
res.json(result);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error('[HTTP Server] validate-coverage-claim error:', error);
|
|
173
|
+
res.status(400).json(this.createErrorResponse(error instanceof Error ? error.message : 'Unknown error'));
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Get safeguard details endpoint
|
|
178
|
+
this.app.get('/api/safeguards/:safeguardId', (req, res) => {
|
|
179
|
+
try {
|
|
180
|
+
const { safeguardId } = req.params;
|
|
181
|
+
const includeExamples = req.query.include_examples === 'true';
|
|
182
|
+
|
|
183
|
+
this.safeguardManager.validateSafeguardId(safeguardId);
|
|
184
|
+
|
|
185
|
+
const safeguard = this.safeguardManager.getSafeguardDetails(safeguardId, includeExamples);
|
|
186
|
+
if (!safeguard) {
|
|
187
|
+
return res.status(404).json(this.createErrorResponse('Safeguard not found'));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
res.json(safeguard);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.error('[HTTP Server] get-safeguard-details error:', error);
|
|
193
|
+
res.status(400).json(this.createErrorResponse(error instanceof Error ? error.message : 'Unknown error'));
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// List all safeguards endpoint
|
|
198
|
+
this.app.get('/api/safeguards', (req, res) => {
|
|
199
|
+
try {
|
|
200
|
+
const safeguards = this.safeguardManager.listAvailableSafeguards();
|
|
201
|
+
|
|
202
|
+
res.json({
|
|
203
|
+
safeguards,
|
|
204
|
+
total: safeguards.length,
|
|
205
|
+
framework: 'CIS Controls v8.1',
|
|
206
|
+
timestamp: new Date().toISOString()
|
|
207
|
+
});
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error('[HTTP Server] list-safeguards error:', error);
|
|
210
|
+
res.status(500).json(this.createErrorResponse('Internal server error'));
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Performance metrics endpoint
|
|
215
|
+
this.app.get('/api/metrics', (req, res) => {
|
|
216
|
+
try {
|
|
217
|
+
const metrics = this.capabilityAnalyzer.getPerformanceMetrics();
|
|
218
|
+
|
|
219
|
+
res.json({
|
|
220
|
+
uptime_seconds: Math.round((Date.now() - metrics.uptime) / 1000),
|
|
221
|
+
total_requests: metrics.totalRequests,
|
|
222
|
+
error_count: metrics.errorCount,
|
|
223
|
+
error_rate: metrics.totalRequests > 0 ?
|
|
224
|
+
((metrics.errorCount / metrics.totalRequests) * 100).toFixed(2) + '%' : '0%',
|
|
225
|
+
request_counts: Object.fromEntries(metrics.requestCounts),
|
|
226
|
+
timestamp: new Date().toISOString()
|
|
227
|
+
});
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error('[HTTP Server] metrics error:', error);
|
|
230
|
+
res.status(500).json(this.createErrorResponse('Metrics unavailable'));
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// API documentation endpoint
|
|
235
|
+
this.app.get('/api', (req, res) => {
|
|
236
|
+
res.json({
|
|
237
|
+
name: 'Framework MCP HTTP API',
|
|
238
|
+
version: '1.2.0',
|
|
239
|
+
description: 'Dual-architecture HTTP API for vendor capability assessment against CIS Controls Framework',
|
|
240
|
+
endpoints: {
|
|
241
|
+
'POST /api/validate-vendor-mapping': 'Primary capability validation with domain validation',
|
|
242
|
+
'POST /api/analyze-vendor-response': 'Capability role determination for vendor responses',
|
|
243
|
+
'POST /api/validate-coverage-claim': 'Validate FULL/PARTIAL implementation claims',
|
|
244
|
+
'GET /api/safeguards': 'List all available CIS safeguards',
|
|
245
|
+
'GET /api/safeguards/:id': 'Get detailed safeguard breakdown',
|
|
246
|
+
'GET /health': 'Health check endpoint',
|
|
247
|
+
'GET /api/metrics': 'Performance metrics',
|
|
248
|
+
'GET /api': 'This documentation'
|
|
249
|
+
},
|
|
250
|
+
capabilities: [
|
|
251
|
+
'FULL - Complete implementation of safeguard',
|
|
252
|
+
'PARTIAL - Limited scope implementation',
|
|
253
|
+
'FACILITATES - Enhancement capabilities',
|
|
254
|
+
'GOVERNANCE - Policy/process management',
|
|
255
|
+
'VALIDATES - Evidence collection and reporting'
|
|
256
|
+
],
|
|
257
|
+
domain_validation: 'Auto-downgrade protection for inappropriate capability claims',
|
|
258
|
+
framework: 'CIS Controls v8.1 (153 safeguards)',
|
|
259
|
+
deployment: 'DigitalOcean App Services compatible'
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Root endpoint redirect
|
|
264
|
+
this.app.get('/', (req, res) => {
|
|
265
|
+
res.redirect('/api');
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private setupErrorHandling(): void {
|
|
270
|
+
// 404 handler
|
|
271
|
+
this.app.use('*', (req, res) => {
|
|
272
|
+
res.status(404).json(this.createErrorResponse(
|
|
273
|
+
`Endpoint not found: ${req.method} ${req.originalUrl}`,
|
|
274
|
+
'Use GET /api for available endpoints'
|
|
275
|
+
));
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Global error handler
|
|
279
|
+
this.app.use((error: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
280
|
+
console.error('[HTTP Server] Unhandled error:', error);
|
|
281
|
+
|
|
282
|
+
if (!res.headersSent) {
|
|
283
|
+
res.status(500).json(this.createErrorResponse(
|
|
284
|
+
'Internal server error',
|
|
285
|
+
process.env.NODE_ENV === 'development' ? error.message : undefined
|
|
286
|
+
));
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private validateInput(body: any, requiredFields: string[]): void {
|
|
292
|
+
if (!body || typeof body !== 'object') {
|
|
293
|
+
throw new Error('Request body is required and must be valid JSON');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
for (const field of requiredFields) {
|
|
297
|
+
if (!body[field] || typeof body[field] !== 'string') {
|
|
298
|
+
throw new Error(`Field '${field}' is required and must be a non-empty string`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private validateTextInput(text: string, fieldName: string): void {
|
|
304
|
+
if (typeof text !== 'string') {
|
|
305
|
+
throw new Error(`${fieldName} must be a string`);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (text.length < 10) {
|
|
309
|
+
throw new Error(`${fieldName} must be at least 10 characters long`);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (text.length > 10000) {
|
|
313
|
+
throw new Error(`${fieldName} must be less than 10,000 characters`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private validateCapability(capability: string): void {
|
|
318
|
+
const validCapabilities = ['full', 'partial', 'facilitates', 'governance', 'validates'];
|
|
319
|
+
|
|
320
|
+
if (!validCapabilities.includes(capability.toLowerCase())) {
|
|
321
|
+
throw new Error(`Invalid capability '${capability}'. Valid options: ${validCapabilities.join(', ')}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private createErrorResponse(error: string, details?: string): ErrorResponse {
|
|
326
|
+
return {
|
|
327
|
+
error,
|
|
328
|
+
details,
|
|
329
|
+
timestamp: new Date().toISOString()
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
public start(): void {
|
|
334
|
+
this.app.listen(this.port, '0.0.0.0', () => {
|
|
335
|
+
console.log(`🚀 Framework MCP HTTP Server v1.2.0 running on port ${this.port}`);
|
|
336
|
+
console.log(`📊 Health check: http://localhost:${this.port}/health`);
|
|
337
|
+
console.log(`📖 API docs: http://localhost:${this.port}/api`);
|
|
338
|
+
console.log(`🔧 Environment: ${process.env.NODE_ENV || 'development'}`);
|
|
339
|
+
console.log(`⚡ DigitalOcean App Services compatible`);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// Graceful shutdown handling
|
|
343
|
+
process.on('SIGTERM', () => {
|
|
344
|
+
console.log('🔄 Received SIGTERM, shutting down gracefully...');
|
|
345
|
+
process.exit(0);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
process.on('SIGINT', () => {
|
|
349
|
+
console.log('🔄 Received SIGINT, shutting down gracefully...');
|
|
350
|
+
process.exit(0);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Start server if this file is run directly
|
|
356
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
357
|
+
const port = parseInt(process.env.PORT || '8080', 10);
|
|
358
|
+
const server = new FrameworkHttpServer(port);
|
|
359
|
+
server.start();
|
|
360
|
+
}
|