code-sentinel-mcp 0.2.5 → 0.2.6
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/build/index.js +661 -1
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import { analyzeSecurityIssues } from './analyzers/security.js';
|
|
6
6
|
import { analyzeDeceptivePatterns } from './analyzers/deceptive.js';
|
|
7
7
|
import { analyzePlaceholders } from './analyzers/placeholders.js';
|
|
@@ -10,6 +10,7 @@ import { analyzeStrengths } from './analyzers/strengths.js';
|
|
|
10
10
|
import { analyzePatterns, inferLevelFromQuery } from './analyzers/patterns.js';
|
|
11
11
|
import { analyzeDesignPatterns, formatDesignAnalysis } from './analyzers/patterns.js';
|
|
12
12
|
import { generateHtmlReport } from './report.js';
|
|
13
|
+
import { getDefinitions, getPatternStats } from './patterns/index.js';
|
|
13
14
|
// Detect language from filename
|
|
14
15
|
function detectLanguage(filename) {
|
|
15
16
|
const ext = filename.split('.').pop()?.toLowerCase() || '';
|
|
@@ -84,6 +85,8 @@ const server = new Server({
|
|
|
84
85
|
}, {
|
|
85
86
|
capabilities: {
|
|
86
87
|
tools: {},
|
|
88
|
+
prompts: {},
|
|
89
|
+
resources: {},
|
|
87
90
|
},
|
|
88
91
|
});
|
|
89
92
|
// List available tools
|
|
@@ -106,6 +109,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
106
109
|
}
|
|
107
110
|
},
|
|
108
111
|
required: ["code", "filename"]
|
|
112
|
+
},
|
|
113
|
+
annotations: {
|
|
114
|
+
title: "Full Code Analysis",
|
|
115
|
+
readOnlyHint: true,
|
|
116
|
+
destructiveHint: false,
|
|
117
|
+
idempotentHint: true,
|
|
118
|
+
openWorldHint: false
|
|
109
119
|
}
|
|
110
120
|
},
|
|
111
121
|
{
|
|
@@ -124,6 +134,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
124
134
|
}
|
|
125
135
|
},
|
|
126
136
|
required: ["code", "filename"]
|
|
137
|
+
},
|
|
138
|
+
annotations: {
|
|
139
|
+
title: "Generate HTML Report",
|
|
140
|
+
readOnlyHint: true,
|
|
141
|
+
destructiveHint: false,
|
|
142
|
+
idempotentHint: true,
|
|
143
|
+
openWorldHint: false
|
|
127
144
|
}
|
|
128
145
|
},
|
|
129
146
|
{
|
|
@@ -142,6 +159,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
142
159
|
}
|
|
143
160
|
},
|
|
144
161
|
required: ["code", "filename"]
|
|
162
|
+
},
|
|
163
|
+
annotations: {
|
|
164
|
+
title: "Security Vulnerability Check",
|
|
165
|
+
readOnlyHint: true,
|
|
166
|
+
destructiveHint: false,
|
|
167
|
+
idempotentHint: true,
|
|
168
|
+
openWorldHint: false
|
|
145
169
|
}
|
|
146
170
|
},
|
|
147
171
|
{
|
|
@@ -160,6 +184,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
160
184
|
}
|
|
161
185
|
},
|
|
162
186
|
required: ["code", "filename"]
|
|
187
|
+
},
|
|
188
|
+
annotations: {
|
|
189
|
+
title: "Deceptive Pattern Check",
|
|
190
|
+
readOnlyHint: true,
|
|
191
|
+
destructiveHint: false,
|
|
192
|
+
idempotentHint: true,
|
|
193
|
+
openWorldHint: false
|
|
163
194
|
}
|
|
164
195
|
},
|
|
165
196
|
{
|
|
@@ -178,6 +209,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
178
209
|
}
|
|
179
210
|
},
|
|
180
211
|
required: ["code", "filename"]
|
|
212
|
+
},
|
|
213
|
+
annotations: {
|
|
214
|
+
title: "Placeholder Code Check",
|
|
215
|
+
readOnlyHint: true,
|
|
216
|
+
destructiveHint: false,
|
|
217
|
+
idempotentHint: true,
|
|
218
|
+
openWorldHint: false
|
|
181
219
|
}
|
|
182
220
|
},
|
|
183
221
|
{
|
|
@@ -205,6 +243,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
205
243
|
}
|
|
206
244
|
},
|
|
207
245
|
required: ["code", "filename"]
|
|
246
|
+
},
|
|
247
|
+
annotations: {
|
|
248
|
+
title: "Architecture Pattern Analysis",
|
|
249
|
+
readOnlyHint: true,
|
|
250
|
+
destructiveHint: false,
|
|
251
|
+
idempotentHint: true,
|
|
252
|
+
openWorldHint: false
|
|
208
253
|
}
|
|
209
254
|
},
|
|
210
255
|
{
|
|
@@ -223,6 +268,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
223
268
|
}
|
|
224
269
|
},
|
|
225
270
|
required: ["code", "filename"]
|
|
271
|
+
},
|
|
272
|
+
annotations: {
|
|
273
|
+
title: "GoF Design Pattern Analysis",
|
|
274
|
+
readOnlyHint: true,
|
|
275
|
+
destructiveHint: false,
|
|
276
|
+
idempotentHint: true,
|
|
277
|
+
openWorldHint: false
|
|
226
278
|
}
|
|
227
279
|
}
|
|
228
280
|
]
|
|
@@ -402,6 +454,614 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
402
454
|
};
|
|
403
455
|
}
|
|
404
456
|
});
|
|
457
|
+
// List available prompts
|
|
458
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
459
|
+
return {
|
|
460
|
+
prompts: [
|
|
461
|
+
{
|
|
462
|
+
name: "analyze-and-report",
|
|
463
|
+
description: "Analyze code and generate a comprehensive markdown report with actionable items for fixing issues",
|
|
464
|
+
arguments: [
|
|
465
|
+
{
|
|
466
|
+
name: "code",
|
|
467
|
+
description: "The source code to analyze",
|
|
468
|
+
required: true
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
name: "filename",
|
|
472
|
+
description: "The filename for language detection (e.g., 'app.ts')",
|
|
473
|
+
required: true
|
|
474
|
+
}
|
|
475
|
+
]
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
name: "fix-issues",
|
|
479
|
+
description: "Analyze code and generate step-by-step fix instructions that a coding agent can execute",
|
|
480
|
+
arguments: [
|
|
481
|
+
{
|
|
482
|
+
name: "code",
|
|
483
|
+
description: "The source code to analyze",
|
|
484
|
+
required: true
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
name: "filename",
|
|
488
|
+
description: "The filename for language detection (e.g., 'app.ts')",
|
|
489
|
+
required: true
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
name: "category",
|
|
493
|
+
description: "Optional: Focus on specific category (security, deceptive, placeholder, error)",
|
|
494
|
+
required: false
|
|
495
|
+
}
|
|
496
|
+
]
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
name: "security-audit",
|
|
500
|
+
description: "Perform a security-focused audit with remediation steps",
|
|
501
|
+
arguments: [
|
|
502
|
+
{
|
|
503
|
+
name: "code",
|
|
504
|
+
description: "The source code to audit",
|
|
505
|
+
required: true
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
name: "filename",
|
|
509
|
+
description: "The filename for language detection",
|
|
510
|
+
required: true
|
|
511
|
+
}
|
|
512
|
+
]
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
name: "pre-commit-check",
|
|
516
|
+
description: "Quick analysis suitable for pre-commit hooks - focuses on critical and high severity issues",
|
|
517
|
+
arguments: [
|
|
518
|
+
{
|
|
519
|
+
name: "code",
|
|
520
|
+
description: "The source code to check",
|
|
521
|
+
required: true
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
name: "filename",
|
|
525
|
+
description: "The filename for language detection",
|
|
526
|
+
required: true
|
|
527
|
+
}
|
|
528
|
+
]
|
|
529
|
+
}
|
|
530
|
+
]
|
|
531
|
+
};
|
|
532
|
+
});
|
|
533
|
+
// Handle prompt requests
|
|
534
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
535
|
+
const { name, arguments: args } = request.params;
|
|
536
|
+
const code = args?.code || '';
|
|
537
|
+
const filename = args?.filename || 'unknown.ts';
|
|
538
|
+
const category = args?.category || '';
|
|
539
|
+
// Run analysis
|
|
540
|
+
const result = analyzeCode(code, filename);
|
|
541
|
+
const score = Math.max(0, 100 - (result.summary.critical * 25 + result.summary.high * 15 + result.summary.medium * 5 + result.summary.low));
|
|
542
|
+
switch (name) {
|
|
543
|
+
case "analyze-and-report": {
|
|
544
|
+
const issuesByCategory = {
|
|
545
|
+
critical: result.issues.filter(i => i.severity === 'critical'),
|
|
546
|
+
high: result.issues.filter(i => i.severity === 'high'),
|
|
547
|
+
medium: result.issues.filter(i => i.severity === 'medium'),
|
|
548
|
+
low: result.issues.filter(i => i.severity === 'low')
|
|
549
|
+
};
|
|
550
|
+
let markdown = `# CodeSentinel Analysis Report
|
|
551
|
+
|
|
552
|
+
## File: ${filename}
|
|
553
|
+
## Language: ${result.language}
|
|
554
|
+
## Quality Score: ${score}/100
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## Summary
|
|
559
|
+
|
|
560
|
+
| Severity | Count |
|
|
561
|
+
|:---------|:------|
|
|
562
|
+
| Critical | ${result.summary.critical} |
|
|
563
|
+
| High | ${result.summary.high} |
|
|
564
|
+
| Medium | ${result.summary.medium} |
|
|
565
|
+
| Low | ${result.summary.low} |
|
|
566
|
+
| **Strengths** | ${result.summary.strengths} |
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## Issues Found
|
|
571
|
+
|
|
572
|
+
`;
|
|
573
|
+
if (issuesByCategory.critical.length > 0) {
|
|
574
|
+
markdown += `### Critical Issues (Fix Immediately)\n\n`;
|
|
575
|
+
issuesByCategory.critical.forEach(issue => {
|
|
576
|
+
markdown += `- **[${issue.id}] ${issue.title}** (Line ${issue.line || 'N/A'})\n`;
|
|
577
|
+
markdown += ` - ${issue.description}\n`;
|
|
578
|
+
markdown += ` - **Fix:** ${issue.suggestion || 'Review and fix manually'}\n`;
|
|
579
|
+
if (issue.code)
|
|
580
|
+
markdown += ` - Code: \`${issue.code}\`\n`;
|
|
581
|
+
markdown += `\n`;
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
if (issuesByCategory.high.length > 0) {
|
|
585
|
+
markdown += `### High Priority Issues\n\n`;
|
|
586
|
+
issuesByCategory.high.forEach(issue => {
|
|
587
|
+
markdown += `- **[${issue.id}] ${issue.title}** (Line ${issue.line || 'N/A'})\n`;
|
|
588
|
+
markdown += ` - ${issue.description}\n`;
|
|
589
|
+
markdown += ` - **Fix:** ${issue.suggestion || 'Review and fix manually'}\n`;
|
|
590
|
+
markdown += `\n`;
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
if (issuesByCategory.medium.length > 0) {
|
|
594
|
+
markdown += `### Medium Priority Issues\n\n`;
|
|
595
|
+
issuesByCategory.medium.forEach(issue => {
|
|
596
|
+
markdown += `- **[${issue.id}] ${issue.title}** (Line ${issue.line || 'N/A'})\n`;
|
|
597
|
+
markdown += ` - ${issue.description}\n`;
|
|
598
|
+
markdown += ` - **Fix:** ${issue.suggestion || 'Review and fix manually'}\n`;
|
|
599
|
+
markdown += `\n`;
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
if (issuesByCategory.low.length > 0) {
|
|
603
|
+
markdown += `### Low Priority Issues\n\n`;
|
|
604
|
+
issuesByCategory.low.forEach(issue => {
|
|
605
|
+
markdown += `- **[${issue.id}] ${issue.title}** (Line ${issue.line || 'N/A'})\n`;
|
|
606
|
+
markdown += ` - ${issue.description}\n`;
|
|
607
|
+
markdown += `\n`;
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
if (result.strengths.length > 0) {
|
|
611
|
+
markdown += `---\n\n## Strengths Detected\n\n`;
|
|
612
|
+
result.strengths.forEach(s => {
|
|
613
|
+
markdown += `- **${s.title}**: ${s.description}\n`;
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
messages: [
|
|
618
|
+
{
|
|
619
|
+
role: "user",
|
|
620
|
+
content: {
|
|
621
|
+
type: "text",
|
|
622
|
+
text: markdown
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
]
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
case "fix-issues": {
|
|
629
|
+
let issues = result.issues;
|
|
630
|
+
// Filter by category if specified
|
|
631
|
+
if (category) {
|
|
632
|
+
issues = issues.filter(i => i.category === category);
|
|
633
|
+
}
|
|
634
|
+
// Focus on critical and high first
|
|
635
|
+
const priorityIssues = issues.filter(i => i.severity === 'critical' || i.severity === 'high');
|
|
636
|
+
const otherIssues = issues.filter(i => i.severity !== 'critical' && i.severity !== 'high');
|
|
637
|
+
let prompt = `# Code Fix Instructions for ${filename}
|
|
638
|
+
|
|
639
|
+
You are a coding agent. The following issues were detected in the code. Fix them in order of priority.
|
|
640
|
+
|
|
641
|
+
## Analysis Summary
|
|
642
|
+
- Total issues: ${issues.length}
|
|
643
|
+
- Critical: ${issues.filter(i => i.severity === 'critical').length}
|
|
644
|
+
- High: ${issues.filter(i => i.severity === 'high').length}
|
|
645
|
+
- Medium: ${issues.filter(i => i.severity === 'medium').length}
|
|
646
|
+
- Low: ${issues.filter(i => i.severity === 'low').length}
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
## Priority Fixes (Do These First)
|
|
651
|
+
|
|
652
|
+
`;
|
|
653
|
+
priorityIssues.forEach((issue, index) => {
|
|
654
|
+
prompt += `### Fix ${index + 1}: ${issue.title}
|
|
655
|
+
- **ID:** ${issue.id}
|
|
656
|
+
- **Severity:** ${issue.severity.toUpperCase()}
|
|
657
|
+
- **Line:** ${issue.line || 'Unknown'}
|
|
658
|
+
- **Problem:** ${issue.description}
|
|
659
|
+
- **Current Code:** \`${issue.code || 'N/A'}\`
|
|
660
|
+
- **Action Required:** ${issue.suggestion || 'Fix this issue'}
|
|
661
|
+
|
|
662
|
+
`;
|
|
663
|
+
});
|
|
664
|
+
if (otherIssues.length > 0) {
|
|
665
|
+
prompt += `---
|
|
666
|
+
|
|
667
|
+
## Additional Fixes (Lower Priority)
|
|
668
|
+
|
|
669
|
+
`;
|
|
670
|
+
otherIssues.forEach((issue, index) => {
|
|
671
|
+
prompt += `### ${index + 1}. [${issue.severity.toUpperCase()}] ${issue.title}
|
|
672
|
+
- Line ${issue.line || 'Unknown'}: ${issue.description}
|
|
673
|
+
- Fix: ${issue.suggestion || 'Review and fix'}
|
|
674
|
+
|
|
675
|
+
`;
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
prompt += `---
|
|
679
|
+
|
|
680
|
+
## Instructions for Agent
|
|
681
|
+
|
|
682
|
+
1. Read each issue carefully
|
|
683
|
+
2. Locate the line in the code
|
|
684
|
+
3. Apply the suggested fix
|
|
685
|
+
4. Verify the fix doesn't break other functionality
|
|
686
|
+
5. Move to the next issue
|
|
687
|
+
|
|
688
|
+
After fixing all issues, run CodeSentinel analysis again to verify fixes.
|
|
689
|
+
`;
|
|
690
|
+
return {
|
|
691
|
+
messages: [
|
|
692
|
+
{
|
|
693
|
+
role: "user",
|
|
694
|
+
content: {
|
|
695
|
+
type: "text",
|
|
696
|
+
text: prompt
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
]
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
case "security-audit": {
|
|
703
|
+
const securityIssues = result.issues.filter(i => i.category === 'security');
|
|
704
|
+
let prompt = `# Security Audit Report: ${filename}
|
|
705
|
+
|
|
706
|
+
## Overview
|
|
707
|
+
- Security issues found: ${securityIssues.length}
|
|
708
|
+
- Critical: ${securityIssues.filter(i => i.severity === 'critical').length}
|
|
709
|
+
- High: ${securityIssues.filter(i => i.severity === 'high').length}
|
|
710
|
+
- Medium: ${securityIssues.filter(i => i.severity === 'medium').length}
|
|
711
|
+
|
|
712
|
+
`;
|
|
713
|
+
if (securityIssues.length === 0) {
|
|
714
|
+
prompt += `No security vulnerabilities detected. However, this automated scan may not catch all issues. Manual security review is recommended for sensitive code.
|
|
715
|
+
`;
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
prompt += `## Vulnerabilities Found
|
|
719
|
+
|
|
720
|
+
`;
|
|
721
|
+
securityIssues.forEach((issue, index) => {
|
|
722
|
+
prompt += `### ${index + 1}. ${issue.title}
|
|
723
|
+
- **Severity:** ${issue.severity.toUpperCase()}
|
|
724
|
+
- **ID:** ${issue.id}
|
|
725
|
+
- **Line:** ${issue.line || 'Unknown'}
|
|
726
|
+
- **Description:** ${issue.description}
|
|
727
|
+
- **Vulnerable Code:** \`${issue.code || 'N/A'}\`
|
|
728
|
+
- **Remediation:** ${issue.suggestion || 'Fix immediately'}
|
|
729
|
+
`;
|
|
730
|
+
if (issue.verification) {
|
|
731
|
+
prompt += `- **Verification:** ${issue.verification.instruction || 'Manual verification required'}
|
|
732
|
+
`;
|
|
733
|
+
}
|
|
734
|
+
prompt += `\n`;
|
|
735
|
+
});
|
|
736
|
+
prompt += `## Remediation Priority
|
|
737
|
+
|
|
738
|
+
1. Fix all CRITICAL issues immediately - these may be actively exploitable
|
|
739
|
+
2. Address HIGH severity issues before deployment
|
|
740
|
+
3. Review MEDIUM issues in next sprint
|
|
741
|
+
4. LOW issues can be addressed as technical debt
|
|
742
|
+
|
|
743
|
+
## Next Steps
|
|
744
|
+
|
|
745
|
+
1. Fix issues in order of severity
|
|
746
|
+
2. Re-run security audit after fixes
|
|
747
|
+
3. Consider additional security testing (penetration testing, SAST/DAST)
|
|
748
|
+
`;
|
|
749
|
+
}
|
|
750
|
+
return {
|
|
751
|
+
messages: [
|
|
752
|
+
{
|
|
753
|
+
role: "user",
|
|
754
|
+
content: {
|
|
755
|
+
type: "text",
|
|
756
|
+
text: prompt
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
]
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
case "pre-commit-check": {
|
|
763
|
+
const blockingIssues = result.issues.filter(i => i.severity === 'critical' || i.severity === 'high');
|
|
764
|
+
let prompt = `# Pre-Commit Check: ${filename}
|
|
765
|
+
|
|
766
|
+
`;
|
|
767
|
+
if (blockingIssues.length === 0) {
|
|
768
|
+
prompt += `## PASS
|
|
769
|
+
|
|
770
|
+
No critical or high-severity issues detected. Safe to commit.
|
|
771
|
+
|
|
772
|
+
**Score:** ${score}/100
|
|
773
|
+
`;
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
prompt += `## FAIL - ${blockingIssues.length} blocking issue(s)
|
|
777
|
+
|
|
778
|
+
The following issues must be fixed before committing:
|
|
779
|
+
|
|
780
|
+
`;
|
|
781
|
+
blockingIssues.forEach((issue, index) => {
|
|
782
|
+
prompt += `${index + 1}. **[${issue.severity.toUpperCase()}]** ${issue.title} (Line ${issue.line || '?'})
|
|
783
|
+
- ${issue.suggestion || issue.description}
|
|
784
|
+
`;
|
|
785
|
+
});
|
|
786
|
+
prompt += `
|
|
787
|
+
---
|
|
788
|
+
|
|
789
|
+
Fix these issues and run the check again.
|
|
790
|
+
`;
|
|
791
|
+
}
|
|
792
|
+
return {
|
|
793
|
+
messages: [
|
|
794
|
+
{
|
|
795
|
+
role: "user",
|
|
796
|
+
content: {
|
|
797
|
+
type: "text",
|
|
798
|
+
text: prompt
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
]
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
default:
|
|
805
|
+
return {
|
|
806
|
+
messages: [
|
|
807
|
+
{
|
|
808
|
+
role: "user",
|
|
809
|
+
content: {
|
|
810
|
+
type: "text",
|
|
811
|
+
text: `Unknown prompt: ${name}`
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
]
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
// List available resources
|
|
819
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
820
|
+
return {
|
|
821
|
+
resources: [
|
|
822
|
+
{
|
|
823
|
+
uri: "codesentinel://patterns/all",
|
|
824
|
+
name: "All Patterns",
|
|
825
|
+
description: "Complete list of all CodeSentinel detection patterns",
|
|
826
|
+
mimeType: "application/json",
|
|
827
|
+
annotations: {
|
|
828
|
+
audience: ["assistant"],
|
|
829
|
+
priority: 0.8
|
|
830
|
+
}
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
uri: "codesentinel://patterns/security",
|
|
834
|
+
name: "Security Patterns",
|
|
835
|
+
description: "Patterns for detecting security vulnerabilities (hardcoded secrets, SQL injection, XSS, etc.)",
|
|
836
|
+
mimeType: "application/json",
|
|
837
|
+
annotations: {
|
|
838
|
+
audience: ["assistant"],
|
|
839
|
+
priority: 0.9
|
|
840
|
+
}
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
uri: "codesentinel://patterns/deceptive",
|
|
844
|
+
name: "Deceptive Patterns",
|
|
845
|
+
description: "Patterns for detecting error-hiding code (empty catches, silent failures, fake success)",
|
|
846
|
+
mimeType: "application/json",
|
|
847
|
+
annotations: {
|
|
848
|
+
audience: ["assistant"],
|
|
849
|
+
priority: 0.9
|
|
850
|
+
}
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
uri: "codesentinel://patterns/placeholder",
|
|
854
|
+
name: "Placeholder Patterns",
|
|
855
|
+
description: "Patterns for detecting incomplete code (TODO, FIXME, dummy data, test values)",
|
|
856
|
+
mimeType: "application/json",
|
|
857
|
+
annotations: {
|
|
858
|
+
audience: ["assistant"],
|
|
859
|
+
priority: 0.7
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
uri: "codesentinel://patterns/error",
|
|
864
|
+
name: "Error Patterns",
|
|
865
|
+
description: "Patterns for detecting code smells and potential bugs (loose equality, null refs, async issues)",
|
|
866
|
+
mimeType: "application/json",
|
|
867
|
+
annotations: {
|
|
868
|
+
audience: ["assistant"],
|
|
869
|
+
priority: 0.8
|
|
870
|
+
}
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
uri: "codesentinel://stats",
|
|
874
|
+
name: "Pattern Statistics",
|
|
875
|
+
description: "Statistics about available patterns by category and severity",
|
|
876
|
+
mimeType: "application/json",
|
|
877
|
+
annotations: {
|
|
878
|
+
audience: ["user", "assistant"],
|
|
879
|
+
priority: 0.5
|
|
880
|
+
}
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
uri: "codesentinel://guide/categories",
|
|
884
|
+
name: "Category Guide",
|
|
885
|
+
description: "Explanation of each pattern category and when issues are detected",
|
|
886
|
+
mimeType: "text/markdown",
|
|
887
|
+
annotations: {
|
|
888
|
+
audience: ["user"],
|
|
889
|
+
priority: 0.6
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
]
|
|
893
|
+
};
|
|
894
|
+
});
|
|
895
|
+
// Read resource content
|
|
896
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
897
|
+
const { uri } = request.params;
|
|
898
|
+
// Pattern by ID: codesentinel://pattern/CS-SEC001
|
|
899
|
+
if (uri.startsWith("codesentinel://pattern/")) {
|
|
900
|
+
const patternId = uri.replace("codesentinel://pattern/", "");
|
|
901
|
+
const allPatterns = getDefinitions();
|
|
902
|
+
const pattern = allPatterns.find(p => p.id === patternId);
|
|
903
|
+
if (!pattern) {
|
|
904
|
+
return {
|
|
905
|
+
contents: [
|
|
906
|
+
{
|
|
907
|
+
uri,
|
|
908
|
+
mimeType: "application/json",
|
|
909
|
+
text: JSON.stringify({ error: `Pattern not found: ${patternId}` })
|
|
910
|
+
}
|
|
911
|
+
]
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
return {
|
|
915
|
+
contents: [
|
|
916
|
+
{
|
|
917
|
+
uri,
|
|
918
|
+
mimeType: "application/json",
|
|
919
|
+
text: JSON.stringify({
|
|
920
|
+
id: pattern.id,
|
|
921
|
+
title: pattern.title,
|
|
922
|
+
description: pattern.description,
|
|
923
|
+
severity: pattern.severity,
|
|
924
|
+
category: pattern.category,
|
|
925
|
+
suggestion: pattern.suggestion,
|
|
926
|
+
matchType: pattern.match.type
|
|
927
|
+
}, null, 2)
|
|
928
|
+
}
|
|
929
|
+
]
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
// All patterns
|
|
933
|
+
if (uri === "codesentinel://patterns/all") {
|
|
934
|
+
const allPatterns = getDefinitions();
|
|
935
|
+
const simplified = allPatterns.map(p => ({
|
|
936
|
+
id: p.id,
|
|
937
|
+
title: p.title,
|
|
938
|
+
severity: p.severity,
|
|
939
|
+
category: p.category,
|
|
940
|
+
description: p.description
|
|
941
|
+
}));
|
|
942
|
+
return {
|
|
943
|
+
contents: [
|
|
944
|
+
{
|
|
945
|
+
uri,
|
|
946
|
+
mimeType: "application/json",
|
|
947
|
+
text: JSON.stringify(simplified, null, 2)
|
|
948
|
+
}
|
|
949
|
+
]
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
// Patterns by category
|
|
953
|
+
if (uri.startsWith("codesentinel://patterns/")) {
|
|
954
|
+
const category = uri.replace("codesentinel://patterns/", "");
|
|
955
|
+
const patterns = getDefinitions(category);
|
|
956
|
+
const simplified = patterns.map(p => ({
|
|
957
|
+
id: p.id,
|
|
958
|
+
title: p.title,
|
|
959
|
+
severity: p.severity,
|
|
960
|
+
description: p.description,
|
|
961
|
+
suggestion: p.suggestion
|
|
962
|
+
}));
|
|
963
|
+
return {
|
|
964
|
+
contents: [
|
|
965
|
+
{
|
|
966
|
+
uri,
|
|
967
|
+
mimeType: "application/json",
|
|
968
|
+
text: JSON.stringify(simplified, null, 2)
|
|
969
|
+
}
|
|
970
|
+
]
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
// Stats
|
|
974
|
+
if (uri === "codesentinel://stats") {
|
|
975
|
+
const stats = getPatternStats();
|
|
976
|
+
return {
|
|
977
|
+
contents: [
|
|
978
|
+
{
|
|
979
|
+
uri,
|
|
980
|
+
mimeType: "application/json",
|
|
981
|
+
text: JSON.stringify(stats, null, 2)
|
|
982
|
+
}
|
|
983
|
+
]
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
// Category guide
|
|
987
|
+
if (uri === "codesentinel://guide/categories") {
|
|
988
|
+
const guide = `# CodeSentinel Pattern Categories
|
|
989
|
+
|
|
990
|
+
## Security (CS-SEC)
|
|
991
|
+
Detects vulnerabilities that could be exploited by attackers:
|
|
992
|
+
- Hardcoded secrets (API keys, passwords, tokens)
|
|
993
|
+
- SQL injection via string concatenation
|
|
994
|
+
- XSS via innerHTML or dangerouslySetInnerHTML
|
|
995
|
+
- Insecure cryptography (MD5, SHA1)
|
|
996
|
+
- Disabled SSL validation
|
|
997
|
+
- Wildcard CORS origins
|
|
998
|
+
|
|
999
|
+
**Severity:** Mostly Critical and High
|
|
1000
|
+
**Action:** Fix immediately before deployment
|
|
1001
|
+
|
|
1002
|
+
---
|
|
1003
|
+
|
|
1004
|
+
## Deceptive (CS-DEC)
|
|
1005
|
+
Detects code that hides errors or creates false confidence:
|
|
1006
|
+
- Empty catch blocks that swallow errors
|
|
1007
|
+
- Silent promise rejections
|
|
1008
|
+
- Fallback values that mask failures (\`|| []\`, \`?? {}\`)
|
|
1009
|
+
- Excessive optional chaining
|
|
1010
|
+
- Fake success responses
|
|
1011
|
+
- Linter/type suppression (\`@ts-ignore\`, \`eslint-disable\`)
|
|
1012
|
+
|
|
1013
|
+
**Severity:** Mostly High and Medium
|
|
1014
|
+
**Action:** Review and add proper error handling
|
|
1015
|
+
|
|
1016
|
+
---
|
|
1017
|
+
|
|
1018
|
+
## Placeholder (CS-PH)
|
|
1019
|
+
Detects incomplete implementations and test data:
|
|
1020
|
+
- TODO/FIXME/HACK comments
|
|
1021
|
+
- Lorem ipsum and dummy text
|
|
1022
|
+
- Test emails and passwords
|
|
1023
|
+
- Localhost URLs
|
|
1024
|
+
- Debug console.log statements
|
|
1025
|
+
- Commented-out code
|
|
1026
|
+
|
|
1027
|
+
**Severity:** Mostly Medium and Low
|
|
1028
|
+
**Action:** Complete implementation or remove before release
|
|
1029
|
+
|
|
1030
|
+
---
|
|
1031
|
+
|
|
1032
|
+
## Error (CS-ERR)
|
|
1033
|
+
Detects code smells and potential runtime bugs:
|
|
1034
|
+
- Loose equality (\`==\` instead of \`===\`)
|
|
1035
|
+
- Assignment in conditions
|
|
1036
|
+
- Array mutation during iteration
|
|
1037
|
+
- parseInt without radix
|
|
1038
|
+
- Await in constructor
|
|
1039
|
+
- Floating-point money comparison
|
|
1040
|
+
|
|
1041
|
+
**Severity:** Mixed
|
|
1042
|
+
**Action:** Review and fix based on context
|
|
1043
|
+
`;
|
|
1044
|
+
return {
|
|
1045
|
+
contents: [
|
|
1046
|
+
{
|
|
1047
|
+
uri,
|
|
1048
|
+
mimeType: "text/markdown",
|
|
1049
|
+
text: guide
|
|
1050
|
+
}
|
|
1051
|
+
]
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
// Unknown resource
|
|
1055
|
+
return {
|
|
1056
|
+
contents: [
|
|
1057
|
+
{
|
|
1058
|
+
uri,
|
|
1059
|
+
mimeType: "text/plain",
|
|
1060
|
+
text: `Unknown resource: ${uri}`
|
|
1061
|
+
}
|
|
1062
|
+
]
|
|
1063
|
+
};
|
|
1064
|
+
});
|
|
405
1065
|
// Start the server
|
|
406
1066
|
async function main() {
|
|
407
1067
|
const transport = new StdioServerTransport();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-sentinel-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"mcpName": "io.github.salrad22/code-sentinel",
|
|
5
5
|
"description": "MCP server for code quality analysis - security, errors, deceptive patterns, and placeholder detection",
|
|
6
6
|
"private": false,
|