home / skills / meriley / claude-code-skills / mcp-server-reviewing
npx playbooks add skill meriley/claude-code-skills --skill mcp-server-reviewingReview the files below or copy the command above to add this skill to your agents.
---
name: mcp-server-reviewing
description: Audits MCP servers for security vulnerabilities, missing validation, error handling issues, and production readiness. Use when reviewing MCP PRs or auditing server quality.
version: 1.0.0
---
# MCP Server Reviewing
## Purpose
Audit MCP (Model Context Protocol) servers for security issues, architectural violations, missing validation, and production readiness. Ensure servers follow SDK best practices.
## When to Use This Skill
- Reviewing MCP server code in PRs
- Auditing existing MCP servers for quality
- Pre-release security and quality checks
- Verifying MCP servers follow best practices
## When NOT to Use This Skill
- Writing new MCP servers (use **mcp-server-writing**)
- General TypeScript/Python code review (use language-specific reviewers)
- Reviewing non-MCP server code
---
## Severity Classification
| Severity | Description | Action |
| ------------ | ------------------------------------------------------------ | ------------------------- |
| **CRITICAL** | Security vulnerabilities, secrets exposure, data leaks | Block PR, fix immediately |
| **HIGH** | Missing validation, error swallowing, transport violations | Block PR, require fix |
| **MEDIUM** | Missing descriptions, poor error messages, logging issues | Request fix before merge |
| **LOW** | Style issues, optimization opportunities, minor improvements | Suggest improvements |
---
## Automated Detection Commands
Run these commands to detect common violations. Each command includes the violation code for reference.
### Security Violations (S1-S5)
**S1: Hardcoded Secrets**
```bash
grep -rn "api[_-]\?key\s*[:=]" --include="*.ts" --include="*.py" src/ | grep -v "process\.env\|os\.environ"
```
**S2: Console.log to stdout (breaks stdio transport)**
```bash
grep -rn "console\.log\|print(" --include="*.ts" --include="*.py" src/ | grep -v "console\.error\|sys\.stderr\|\.test\.\|\.spec\."
```
**S3: Missing ReDoS Protection**
```bash
grep -rn "new RegExp\|\.match\|\.replace\|\.split" --include="*.ts" --include="*.py" src/ | grep -v "safeInput\|MAX_INPUT"
```
**S4: Unsafe eval/exec**
```bash
grep -rn "eval(\|exec(\|Function(" --include="*.ts" --include="*.py" src/
```
**S5: SQL Injection Risk**
```bash
grep -rn "query.*\`\|execute.*f\"\|cursor\.execute.*%" --include="*.ts" --include="*.py" src/
```
### Architecture Violations (A1-A5)
**A1: Missing Tool Descriptions**
```bash
grep -rn "name:\s*['\"]" --include="*.ts" src/ -A 3 | grep -B 3 "inputSchema" | grep -v "description"
```
**A2: Missing inputSchema Property Descriptions**
```bash
grep -rn "properties:" --include="*.ts" src/ -A 10 | grep -E "type.*string|type.*number" | grep -v "description"
```
**A3: Tools Without Validation**
```bash
grep -rn "request\.params\|args\." --include="*.ts" src/ | grep -v "validate\|ajv\|zod\|schema"
```
**A4: Missing isError Flag**
```bash
grep -rn "content.*error\|success.*false" --include="*.ts" src/ | grep -v "isError"
```
**A5: Direct Response Without JSON**
```bash
grep -rn "text:.*\`" --include="*.ts" src/ | grep -v "JSON\.stringify"
```
### Error Handling Violations (E1-E5)
**E1: Empty Catch Blocks**
```bash
grep -rn "catch.*{" --include="*.ts" --include="*.py" src/ -A 2 | grep -E "^\s*}\s*$"
```
**E2: Swallowed Errors (catch without rethrow or return)**
```bash
grep -rn "catch" --include="*.ts" src/ -A 5 | grep -v "throw\|return\|logger\|console\.error"
```
**E3: Missing Error Context**
```bash
grep -rn "throw new Error\|raise.*Error" --include="*.ts" --include="*.py" src/ | grep -v ":\|context\|failed"
```
**E4: Generic Error Messages**
```bash
grep -rn "\"error\"\|\"Error\"\|\"failed\"" --include="*.ts" src/ | grep -v "suggestion\|field\|message"
```
**E5: Unhandled Promise Rejections**
```bash
grep -rn "\.then(" --include="*.ts" src/ | grep -v "\.catch\|await"
```
### Maintainability Violations (M1-M5)
**M1: Magic Numbers/Strings**
```bash
grep -rn "[0-9]\{4,\}\|timeout.*[0-9]" --include="*.ts" --include="*.py" src/ | grep -v "const\|MAX_\|MIN_\|DEFAULT_"
```
**M2: Missing Type Annotations (Python)**
```bash
grep -rn "def.*(" --include="*.py" src/ | grep -v "->.*:"
```
**M3: TODO/FIXME in Production Code**
```bash
grep -rn "TODO\|FIXME\|XXX\|HACK" --include="*.ts" --include="*.py" src/
```
**M4: Duplicate Tool Definitions**
```bash
grep -rn "name:\s*['\"]" --include="*.ts" src/ | cut -d: -f3 | sort | uniq -d
```
**M5: Inconsistent Error Response Format**
```bash
grep -rn "isError.*true" --include="*.ts" src/ -B 5 | grep "text:" | grep -v "success\|errors"
```
---
## Manual Review Checklist
### Security Review
- [ ] **S1**: No hardcoded API keys, passwords, or secrets
- [ ] **S2**: All logging goes to stderr (not stdout)
- [ ] **S3**: Input length limited before regex processing
- [ ] **S4**: No eval(), exec(), or dynamic code execution
- [ ] **S5**: Parameterized queries for any database access
- [ ] **S6**: Environment variables used for configuration
- [ ] **S7**: Sensitive data not logged (passwords, tokens, PII)
### Architecture Review
- [ ] **A1**: All tools have clear `description` fields
- [ ] **A2**: All inputSchema properties have `description`
- [ ] **A3**: Input validation before any processing
- [ ] **A4**: Error responses include `isError: true`
- [ ] **A5**: All responses are JSON-serialized
- [ ] **A6**: Resources use appropriate URI schemes
- [ ] **A7**: Prompts have clear argument descriptions
### Error Handling Review
- [ ] **E1**: No empty catch blocks
- [ ] **E2**: Errors logged or re-thrown, never swallowed
- [ ] **E3**: Error messages include context (what failed, why)
- [ ] **E4**: Error responses include `suggestion` field
- [ ] **E5**: Async operations properly awaited/handled
- [ ] **E6**: Graceful degradation for external dependencies
### Production Readiness Review
- [ ] **P1**: Structured logging with JSON format
- [ ] **P2**: Health check endpoint or mechanism
- [ ] **P3**: Configurable timeouts for external calls
- [ ] **P4**: Graceful shutdown handling
- [ ] **P5**: No debug/development code in production
- [ ] **P6**: Dependencies pinned to specific versions
- [ ] **P7**: Dockerfile or deployment configuration present
---
## Common Violations with Fixes
### S2: Logging to stdout (CRITICAL)
**Problem:** stdout is reserved for JSON-RPC messages. Logging there corrupts the transport.
```typescript
// ❌ BAD: Breaks MCP stdio transport
console.log("Processing request:", args);
```
```typescript
// ✅ GOOD: Log to stderr
console.error(
JSON.stringify({
timestamp: new Date().toISOString(),
level: "INFO",
message: "Processing request",
context: { args_keys: Object.keys(args) },
}),
);
```
### S3: Missing ReDoS Protection (HIGH)
**Problem:** Unbounded input to regex can cause catastrophic backtracking.
```typescript
// ❌ BAD: No input length limit
const matches = userInput.match(/complex.*pattern/);
```
```typescript
// ✅ GOOD: Limit input before regex
const MAX_INPUT_LENGTH = 50_000;
const safeInput = (text: string) =>
text.length > MAX_INPUT_LENGTH ? text.slice(0, MAX_INPUT_LENGTH) : text;
const matches = safeInput(userInput).match(/complex.*pattern/);
```
### A1: Missing Tool Description (MEDIUM)
**Problem:** LLM can't understand when to use the tool.
```typescript
// ❌ BAD: No description
{
name: "process_data",
inputSchema: { type: "object", properties: { data: { type: "string" } } }
}
```
```typescript
// ✅ GOOD: Clear description explaining purpose and return value
{
name: "process_data",
description: "Validates and transforms input data according to specified format. Returns structured result with validation errors if any.",
inputSchema: {
type: "object",
properties: {
data: {
type: "string",
description: "Raw data string to process"
},
format: {
type: "string",
enum: ["json", "csv", "xml"],
description: "Expected input format for validation"
}
},
required: ["data", "format"]
}
}
```
### A4: Missing isError Flag (HIGH)
**Problem:** Client can't distinguish errors from successful responses.
```typescript
// ❌ BAD: Error without isError flag
return {
content: [{ type: "text", text: JSON.stringify({ error: "Not found" }) }],
};
```
```typescript
// ✅ GOOD: Proper error response
return {
content: [
{
type: "text",
text: JSON.stringify({
success: false,
errors: [
{
field: "id",
message: "Resource not found",
suggestion: "Verify the ID exists and try again",
},
],
}),
},
],
isError: true,
};
```
### E1: Empty Catch Block (HIGH)
**Problem:** Errors are silently swallowed, making debugging impossible.
```typescript
// ❌ BAD: Silent failure
try {
await riskyOperation();
} catch (error) {
// do nothing
}
```
```typescript
// ✅ GOOD: Log and handle appropriately
try {
await riskyOperation();
} catch (error) {
logger.error("Risky operation failed", { error: error.message });
return createErrorResponse([
{
field: "operation",
message: "Operation failed",
suggestion: "Check input parameters and retry",
},
]);
}
```
### E4: Generic Error Messages (MEDIUM)
**Problem:** Users can't understand what went wrong or how to fix it.
```typescript
// ❌ BAD: Unhelpful error
return { error: "Something went wrong" };
```
```typescript
// ✅ GOOD: Specific, actionable error
return {
success: false,
errors: [
{
field: "partner_id",
message: "Invalid UUID format: 'abc123'",
suggestion: "Use format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
},
],
};
```
---
## Review Report Template
Use this template when reporting review findings:
```markdown
# MCP Server Review: [Server Name]
**Reviewer:** [Name]
**Date:** [Date]
**Files Reviewed:** [List of files]
## Summary
| Severity | Count |
| -------- | ----- |
| CRITICAL | X |
| HIGH | X |
| MEDIUM | X |
| LOW | X |
**Recommendation:** [APPROVE / APPROVE WITH CHANGES / REQUEST CHANGES / BLOCK]
## Critical Issues
### [S1] Hardcoded API Key
- **File:** src/config.ts:42
- **Code:** `const API_KEY = "sk_live_..."`
- **Fix:** Move to environment variable
## High Issues
### [A4] Missing isError Flag
- **File:** src/tools/processor.ts:128
- **Code:** Error response without isError
- **Fix:** Add `isError: true` to error responses
## Medium Issues
### [A1] Missing Tool Description
- **File:** src/index.ts:56
- **Tool:** `validate_input`
- **Fix:** Add description explaining tool purpose
## Low Issues
### [M3] TODO Comment
- **File:** src/utils/parser.ts:89
- **Comment:** `// TODO: optimize this`
- **Suggestion:** Create ticket or remove
## Checklist Results
- [x] Security: 6/7 passed
- [x] Architecture: 5/7 passed
- [ ] Error Handling: 4/6 passed
- [x] Production: 7/7 passed
```
---
## Workflow
### Step 1: Run Automated Detection
```bash
# Run all security checks
grep -rn "console\.log\|api[_-]\?key\s*[:=]" --include="*.ts" src/
# Run all architecture checks
grep -rn "name:\s*['\"]" --include="*.ts" src/ -A 5 | grep -v description
```
### Step 2: Manual Checklist Review
Walk through each section of the manual review checklist, marking items as pass/fail.
### Step 3: Code Walkthrough
1. Read each tool definition
2. Trace request handling flow
3. Verify error paths
4. Check resource and prompt definitions
### Step 4: Generate Report
Use the review report template to document findings with specific file locations and code snippets.
### Step 5: Classify and Prioritize
1. Group findings by severity
2. Identify blocking issues (CRITICAL/HIGH)
3. Suggest fixes for each issue
---
## Related Skills
- **mcp-server-writing** - Create new MCP servers
- **security-scan** - General security scanning
- **quality-check** - Code quality checks
## Resources
- [MCP Best Practices](https://modelcontextprotocol.info/docs/best-practices/)
- [MCP FAQs](https://modelcontextprotocol.info/docs/faqs/)
- See **REFERENCE.md** for complete violation catalog
- See **CHECKLISTS/** for printable audit checklists