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.
Files changed (2) hide show
  1. package/build/index.js +661 -1
  2. 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.5",
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,