framework-mcp 1.1.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.do/app.yaml +78 -0
  2. package/COPILOT_INTEGRATION.md +335 -0
  3. package/DEPLOYMENT_GUIDE.md +335 -0
  4. package/README.md +96 -3
  5. package/RELEASE_NOTES_v1.2.0.md +396 -0
  6. package/dist/core/capability-analyzer.d.ts +29 -0
  7. package/dist/core/capability-analyzer.d.ts.map +1 -0
  8. package/dist/core/capability-analyzer.js +568 -0
  9. package/dist/core/capability-analyzer.js.map +1 -0
  10. package/dist/core/safeguard-manager.d.ts +15 -0
  11. package/dist/core/safeguard-manager.d.ts.map +1 -0
  12. package/dist/core/safeguard-manager.js +315 -0
  13. package/dist/core/safeguard-manager.js.map +1 -0
  14. package/dist/index.d.ts +4 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +13 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/interfaces/http/http-server.d.ts +17 -0
  19. package/dist/interfaces/http/http-server.d.ts.map +1 -0
  20. package/dist/interfaces/http/http-server.js +285 -0
  21. package/dist/interfaces/http/http-server.js.map +1 -0
  22. package/dist/interfaces/mcp/mcp-server.d.ts +18 -0
  23. package/dist/interfaces/mcp/mcp-server.d.ts.map +1 -0
  24. package/dist/interfaces/mcp/mcp-server.js +299 -0
  25. package/dist/interfaces/mcp/mcp-server.js.map +1 -0
  26. package/dist/shared/types.d.ts +89 -0
  27. package/dist/shared/types.d.ts.map +1 -0
  28. package/dist/shared/types.js +3 -0
  29. package/dist/shared/types.js.map +1 -0
  30. package/package.json +23 -7
  31. package/src/core/capability-analyzer.ts +708 -0
  32. package/src/core/safeguard-manager.ts +339 -0
  33. package/src/index.ts +17 -0
  34. package/src/interfaces/http/http-server.ts +360 -0
  35. package/src/interfaces/mcp/mcp-server.ts +367 -0
  36. package/src/shared/types.ts +104 -0
  37. package/swagger.json +718 -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
+ }