- Overview
- Security Model
- API Key Management
- What We Secure
- What We Don't Secure
- Known Limitations
- Security Boundaries
- Test Servers
- Reporting Vulnerabilities
- Security Best Practices
mcp-verify is a security validation tool for MCP (Model Context Protocol) servers. This document clarifies what security guarantees we provide, what we don't control, and how to use the tool safely.
mcp-verify performs static analysis of MCP servers:
- Schema Validation: Checks JSON-RPC protocol compliance
- Pattern Detection: Scans tool descriptions and schemas for known vulnerability patterns
- Quality Analysis: Evaluates documentation clarity and consistency
- Protocol Compliance: Validates adherence to MCP specification
mcp-verify cannot:
- ❌ Guarantee runtime behavior of tested servers
- ❌ Detect all possible vulnerabilities (security is probabilistic, not absolute)
- ❌ Prevent exploitation of servers it validates
- ❌ Control or manage the security of upstream MCP servers
- ❌ Replace human security audits
- ❌ Guarantee that a "passing" server is production-ready
Critical Understanding: A security score of 100/100 means "no known patterns detected" - it does NOT mean "perfectly secure."
mcp-verify includes optional LLM-powered semantic analysis using Claude API. This feature:
- Costs: ~$0.001 per validation (less than 1 cent)
- Enables: Advanced description-parameter mismatch detection
- Requires: User-managed API key
Important: mcp-verify does NOT store or manage API keys. The user is responsible for security.
When using mcp-verify as a CLI tool:
# User sets environment variable
export ANTHROPIC_API_KEY=sk-ant-api03-...
# mcp-verify reads it at runtime
mcp-verify validate node server.js --semantic-checkSecurity Responsibility: User must secure their environment variables using OS-level protections.
When using mcp-verify as an MCP server within Claude Desktop:
// claude_desktop_config.json
{
"mcpServers": {
"mcp-verify": {
"command": "node",
"args": ["./apps/mcp-server/dist/apps/mcp-server/src/index.js"],
"cwd": "/path/to/mcp-verify",
"env": {
"ANTHROPIC_API_KEY": "sk-ant-api03-..."
}
}
}
}Security Responsibility: Claude Desktop manages the keychain and process isolation. mcp-verify inherits whatever security model Claude Desktop provides.
- Never commit API keys to version control
- Use environment variables for local development
- Use secrets management (AWS Secrets Manager, HashiCorp Vault) in production
- Rotate keys regularly if exposed
- Monitor usage via Anthropic Console
- ✅ Never log API keys (redacted in all logs)
- ✅ Validate key format before API calls
- ✅ Graceful degradation when key is missing
- ✅ Clear user messaging about key requirements
- ✅ No persistent storage (environment variables only)
- ❌ How user stores environment variables
- ❌ Claude Desktop's keychain security
- ❌ Network interception (use HTTPS)
- ❌ API key rotation policies
- ❌ Access control to user's filesystem
mcp-verify detects vulnerability patterns across 61 security rules organized in 6 threat category blocks (OWASP Top 10, MCP-specific, OWASP LLM Top 10, Multi-Agent Attacks, Enterprise Compliance, AI Weaponization). Below are the core OWASP and MCP-specific rules (first 21 of 61 total):
| Code | Rule | Description |
|---|---|---|
| SEC-001 | Authentication Bypass | Weak auth patterns, credential exposure |
| SEC-002 | Command Injection | exec(), eval(), shell commands |
| SEC-003 | SQL Injection | Dynamic SQL queries, unparameterized queries |
| SEC-004 | SSRF | Server-side request forgery patterns |
| SEC-005 | XXE Injection | XML external entity vulnerabilities |
| SEC-006 | Insecure Deserialization | Unsafe object deserialization |
| SEC-007 | Path Traversal | Directory traversal, file access |
| SEC-008 | Data Leakage | API keys, secrets in descriptions |
| SEC-009 | Sensitive Data Exposure | PII, credentials in parameters |
| SEC-010 | Rate Limiting | Missing rate limit protection |
| SEC-011 | ReDoS | Regular expression denial of service |
| SEC-012 | Weak Cryptography | MD5, SHA1, weak algorithms |
| SEC-013 | Prompt Injection | Indirect injection vectors, missing limits |
- Schema validation, required fields, type checking
mcp-verify includes a Security Gateway v1.0 (mcp-verify proxy) - a production-ready, multi-layered defense system that provides real-time threat detection and client-aware panic stop mechanism to prevent DoS attacks.
┌──────────────┐ ┌─────────────────────────────────────────┐ ┌─────────────┐
│ Claude │ → │ mcp-verify Security Gateway v1.0 │ → │ MCP Server │
│ Desktop │ │ ┌─────────────────────────────────────┐ │ │ (Your Code) │
│ │ │ │ 3-Layer Defense System │ │ │ │
│ │ │ │ • Layer 1: Fast Rules (<10ms) │ │ │ │
│ │ │ │ • Layer 2: Suspicious (<50ms) │ │ │ │
│ │ │ │ • Layer 3: LLM (opt-in, 500-2000ms) │ │ │ │
│ │ │ └─────────────────────────────────────┘ │ │ │
│ │ │ ┌─────────────────────────────────────┐ │ │ │
│ │ │ │ Client-Aware Panic Stop │ │ │ │
│ │ │ │ • Map<clientId, state> │ │ │ │
│ │ │ │ • Progressive backoff (30s/60s/∞) │ │ │ │
│ │ │ └─────────────────────────────────────┘ │ │ │
│ │ │ ┌─────────────────────────────────────┐ │ │ │
│ │ │ │ 5 Classic Guardrails │ │ │ │
│ │ │ │ (run after Gateway passes request) │ │ │ │
│ │ │ └─────────────────────────────────────┘ │ │ │
└──────────────┘ └─────────────────────────────────────────┘ └─────────────┘
↓ Security Events
┌─────────────────┐
│ Audit Logs │
│ (JSONL stream) │
└─────────────────┘
The Security Gateway operates transparently with defense-in-depth:
- 3-Layer Defense System - Progressive analysis with early exit on detection
- Client-Aware Panic Stop - Isolated strikes per client to prevent global DoS
- 5 Classic Guardrails - Additional protection layer (HTTPS, Input Sanitization, PII Redaction, Rate Limiting, Command Blocking)
Key Innovation: The 3-layer defense system runs BEFORE the classic guardrails, providing an additional security barrier that blocks threats at the protocol level before they reach traditional defenses.
The Security Gateway implements progressive threat detection with early exit optimization - each layer runs only if previous layers pass the request.
Purpose: Block high-confidence threats with zero false positives using pattern-based detection.
Detection Methods:
- SQL Injection (SEC-001): Detects
OR 1=1,UNION SELECT,'; DROP TABLE, SQL comments - Command Injection (SEC-002): Detects shell metacharacters (
;,|,&,`), command substitution ($()), dangerous commands (rm -rf,del /f) - Path Traversal (SEC-003): Detects
../,..\\, absolute paths (/etc/,C:\)
Characteristics:
- Runtime Analysis: Checks actual parameter values (not just schemas)
- Universal Application: Runs on ALL tools (not filtered by name)
- Guaranteed Latency: <10ms via pure regex (no I/O)
- Zero False Positives: Only blocks confirmed attack patterns
Example:
// Client sends SQL injection
{ method: 'tools/call', params: { name: 'query_users', arguments: { filter: "' OR 1=1--" } } }
// Gateway blocks at Layer 1 (8ms latency)
{
"error": {
"code": -32003,
"message": "Security Gateway blocked request",
"data": {
"blocked": true,
"layer": 1,
"latency_ms": 8,
"findings": [{
"ruleCode": "SEC-001",
"severity": "critical",
"message": "SQL injection detected in parameter 'filter'",
"cwe": "CWE-89",
"owasp": "A03:2021 - Injection",
"remediation": "Use parameterized queries instead of string concatenation",
"matchedPattern": "OR 1=1",
"affectedParameter": "filter"
}]
}
}
}Purpose: Detect complex attack patterns using heuristic scoring and stateful analysis.
Detection Methods:
- Dangerous Tool Chaining (SEC-020): Detects suspicious sequences like
execute→read_file(execute then exfiltrate) - Excessive Permissions (SEC-023): Flags
skipConfirmation: true,bypassValidation: true, missingconfirm: true - Anomaly Detection: Statistical analysis of parameter distributions
Characteristics:
- Stateful Analysis: Tracks tool call history per session
- Heuristic Scoring: Accumulates evidence across multiple parameters
- Configurable Thresholds: Tune sensitivity per deployment
Example:
// Client requests privileged action without confirmation
{ method: 'tools/call', params: { name: 'delete_all', arguments: { skipConfirmation: true } } }
// Gateway blocks at Layer 2 (35ms latency)
{
"error": {
"code": -32003,
"message": "Security Gateway blocked request",
"data": {
"blocked": true,
"layer": 2,
"latency_ms": 35,
"findings": [{
"ruleCode": "SEC-023",
"severity": "high",
"message": "Excessive agency detected: negative confirmation flag",
"remediation": "Require explicit user confirmation for destructive actions"
}]
}
}
}Purpose: Detect novel attacks and semantic threats using AI-powered analysis.
Detection Methods:
- Semantic Prompt Injection: Context-aware analysis of tool call intent
- Polymorphic Attacks: Novel patterns not covered by static rules
- Social Engineering: Context-based manipulation detection
When to Enable:
- ✅ Research environments (studying novel attacks)
- ✅ High-security deployments (military, finance, healthcare)
- ✅ Honeypot/deception systems
- ❌ Production (latency penalty too high - adds 500-2000ms)
- ❌ High-throughput systems (LLM API costs $5-$15 per 1000 requests)
Configuration:
# Enable Layer 3 (disabled by default)
mcp-verify proxy "node server.js" --enable-llm-layer
# Production mode (Layers 1+2 only, <50ms latency)
mcp-verify proxy "node server.js" --no-llm-layerCharacteristics:
- Context-Aware: Understands semantic meaning beyond patterns
- Adaptive: Learns from novel attack vectors
- Explainable: Provides reasoning for each detection
The Panic Stop system prevents Denial of Service (DoS) attacks caused by malicious clients triggering global rate limits.
Without client isolation:
Client A (malicious) → Triggers 3x HTTP 429 errors → GLOBAL panic mode activated
Client B (legitimate) → BLOCKED as collateral damage ❌
Client C (legitimate) → BLOCKED as collateral damage ❌
Each client has isolated strike tracking using Map<clientId, RateLimitState>:
{
strikes: number, // 0-3 strikes
inBackoff: boolean, // Currently in backoff period
blockedUntil: number, // Unix timestamp when backoff expires
panicMode: boolean // Permanently blocked flag
}Client ID Extraction Priority:
x-client-idheader (explicit client identification)x-forwarded-forheader (proxy chain, first IP extracted)req.socket.remoteAddress(direct connection IP)'default-client'(fallback, same as global state)
| Strike | Trigger | Backoff Duration | Behavior | Recovery |
|---|---|---|---|---|
| Strike 1 | First HTTP 429 | 30 seconds | Client blocked temporarily | Auto-resume after 30s |
| Strike 2 | Second HTTP 429 | 60 seconds | Extended block with warning | Auto-resume after 60s |
| Strike 3 | Third HTTP 429 | Permanent | PANIC MODE - client permanently blocked | Only proxy restart clears |
Example Flow:
# Client 192.168.1.100 triggers first 429
[INFO] Strike 1/3 for client 192.168.1.100: Entering 30 second backoff
# Client tries to connect during backoff
{
"error": {
"code": -32005,
"message": "Rate limit backoff active for client 192.168.1.100 (Strike 1/3)",
"data": {
"blockedUntil": "2026-03-07T12:35:30Z",
"remainingSeconds": 25,
"strikes": 1
}
}
}
# After 30s, backoff expires, client auto-resumes
[INFO] Client 192.168.1.100 backoff expired, resuming normal operation
# Client triggers second 429
[WARN] Strike 2/3 for client 192.168.1.100: Entering 60 second backoff
# Client triggers third 429
[ERROR] Strike 3/3 for client 192.168.1.100: PANIC MODE activated
# All subsequent requests permanently blocked
{
"error": {
"code": -32004,
"message": "Proxy is in PANIC MODE for client 192.168.1.100",
"data": {
"reason": "panic_mode",
"strikes": 3,
"panicMode": true
}
}
}Anti-DoS Properties:
- ✅ Client Isolation: One malicious client cannot affect others
- ✅ Self-Healing: Automatic recovery after backoff period
- ✅ Forensic Trail: Complete audit log with client IDs and timestamps
- ✅ Permanent Block: Persistent abusers locked out until proxy restart
The following guardrails run AFTER the Security Gateway passes a request, providing an additional defense layer:
Purpose: Ensures all external HTTP requests are upgraded to HTTPS to prevent man-in-the-middle attacks.
How it works:
- Intercepts tool calls that include URL parameters (e.g.,
fetch_url,download_file) - Detects HTTP URLs using pattern matching:
http://(non-secure) - Automatically upgrades to
https://before forwarding to server - Logs all upgrades for security auditing
Example:
// Client request
{ method: 'tools/call', params: { name: 'fetch_data', arguments: { url: 'http://api.example.com/data' } } }
// Proxy intercepts and upgrades
{ method: 'tools/call', params: { name: 'fetch_data', arguments: { url: 'https://api.example.com/data' } } }
// Audit log
[INFO] 🔒 HTTPS_ENFORCER: Upgraded URL from HTTP to HTTPS: api.example.comProtection against:
- Man-in-the-middle (MITM) attacks
- Credential sniffing on public networks
- API key interception
Limitations:
- Does not protect internal localhost requests (assumed trusted)
- Cannot enforce HTTPS for embedded URLs in text responses
Purpose: Neutralizes common injection attacks by stripping dangerous characters from tool parameters.
How it works:
- Scans all string parameters in tool calls
- Applies multiple sanitization rules:
- SQL Injection: Removes
',",;,--,/*,*/,UNION,SELECT,DROP,DELETE,INSERT - XSS: Strips
<script>,</script>,<iframe>,javascript:,onerror=,onload= - Command Injection: Removes
|,&,;,$(),`,&&,|| - Path Traversal: Strips
../,..\\,/etc/,C:\
- SQL Injection: Removes
- Returns sanitized parameters to tool implementation
Example:
// Malicious client request
{ method: 'tools/call', params: { name: 'search_users', arguments: { query: "' OR 1=1--" } } }
// Proxy sanitizes
{ method: 'tools/call', params: { name: 'search_users', arguments: { query: " OR 11" } } }
// Audit log
[INFO] 🛡️ INPUT_SANITIZER: Sanitized SQL injection attempt in tool 'search_users' (removed: ', --)Protection against:
- SQL Injection (OWASP A03:2021)
- Cross-Site Scripting (OWASP A03:2021)
- Command Injection (OWASP A03:2021)
- Path Traversal (OWASP A01:2021)
Limitations:
- May cause false positives for legitimate queries containing SQL keywords (e.g., "SELECT a product from the menu")
- Cannot detect context-aware injections (e.g., second-order SQL injection)
- Does not validate business logic (e.g., "legal" queries that bypass authorization)
Purpose: Prevents sensitive personal information from leaking in logs and responses.
How it works:
- Scans server responses (not client requests) for PII patterns
- Redacts using regex patterns:
- Credit Cards:
\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}→****-****-****-1234 - SSN:
\d{3}-\d{2}-\d{4}→***-**-1234 - Email:
[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}→***@example.com - API Keys:
sk-[a-zA-Z0-9]{48},ghp_[a-zA-Z0-9]{36}→sk-****...**** - Phone Numbers:
\+?\d{1,3}[-.\s]?\(?\d{1,4}\)?[-.\s]?\d{1,4}[-.\s]?\d{1,9}→***-***-1234
- Credit Cards:
- Leaves last 4 digits visible for debugging
- Redacts both response payloads and audit logs
Example:
// Server response (unredacted)
{ result: { customer: 'John Doe', email: 'john@example.com', card: '4532-1234-5678-9010' } }
// Proxy redacts before logging
{ result: { customer: 'John Doe', email: '***@example.com', card: '****-****-****-9010' } }
// Audit log
[INFO] 🔒 PII_REDACTOR: Redacted 1 email, 1 credit card in responseProtection against:
- Data Leakage (OWASP A01:2021)
- GDPR violations (PII exposure)
- PCI-DSS violations (credit card exposure)
- Compliance violations (HIPAA, CCPA)
Limitations:
- Regex-based detection may miss obfuscated PII (e.g.,
john [at] example.com) - Cannot detect custom PII formats (e.g., internal employee IDs)
- Does not prevent PII from being processed by the server (only redacted in logs)
Purpose: Prevents Denial of Service (DoS) attacks by limiting request frequency per tool.
How it works:
- Implements Token Bucket Algorithm per tool name
- Default limits:
- Capacity: 100 requests per tool
- Refill Rate: 10 tokens per second
- Burst Allowance: 100 requests instantly, then 10/sec sustained
- Tracks requests in memory (resets on proxy restart)
- Rejects excess requests with
429 Too Many Requestserror
Example:
// Client sends 150 requests to 'execute_query' in 1 second
// First 100 requests: ✅ Allowed (bucket has 100 tokens)
// Requests 101-150: ❌ Blocked (bucket empty, refilling at 10/sec)
// Server response
{ error: { code: -32000, message: 'Rate limit exceeded for tool execute_query. Try again in 5 seconds.' } }
// Audit log
[WARN] ⏱️ RATE_LIMITER: Blocked request to 'execute_query' (100/100 tokens used, refill in 5s)Protection against:
- Denial of Service (DoS) attacks
- Resource exhaustion
- Accidental runaway loops (e.g., infinite LLM recursion)
- API quota abuse
Configuration:
# Customize limits via environment variables
MCP_RATE_LIMIT_CAPACITY=200 \
MCP_RATE_LIMIT_REFILL_RATE=20 \
mcp-verify proxy "node server.js"Limitations:
- Per-tool limits, not per-client (all clients share the same bucket)
- No persistent state (limits reset on proxy restart)
- Does not protect against distributed DoS (multiple proxies)
Purpose: Prevents execution of dangerous shell commands that could compromise the host system.
How it works:
- Scans tool parameters for command-like patterns
- Blocks if any of these patterns are detected:
- Destructive:
rm -rf,del /f,format,DROP DATABASE - Network Exfiltration:
curl,wget,nc,netcat,ssh,scp - Privilege Escalation:
sudo,su,chmod 777,chown root - Code Execution:
eval,exec,system(),/bin/sh,cmd.exe
- Destructive:
- Returns error before request reaches server
Example:
// Malicious client request
{ method: 'tools/call', params: { name: 'run_script', arguments: { command: 'rm -rf /var/log/*' } } }
// Proxy blocks
{ error: { code: -32001, message: 'Blocked dangerous command pattern: rm -rf' } }
// Audit log
[WARN] 🚫 SENSITIVE_COMMAND_BLOCKER: Blocked potentially dangerous command pattern: 'rm -rf' in tool 'run_script'Protection against:
- Remote Code Execution (RCE)
- Data Exfiltration
- Privilege Escalation
- System Compromise
Limitations:
- Pattern-based (can be bypassed with obfuscation:
r""m -rf) - Cannot detect context-aware malicious logic (e.g., "legal" command with malicious parameters)
- May block legitimate use cases (e.g.,
curlin a debugging tool)
Bypass Protection: To allow specific commands, use an allow-list:
MCP_ALLOW_COMMANDS="curl,wget" mcp-verify proxy "node server.js"The Proxy emits structured security events to stderr for integration with SIEM/logging systems.
Log Format:
[LEVEL] 🔒 GUARDRAIL_NAME: Event description (metadata)
Log Levels:
[INFO]: Normal security events (sanitization, redaction, HTTPS upgrade)[WARN]: Blocked requests (rate limiting, command blocking)[ERROR]: Internal proxy errors (misconfiguration, guardrail failures)
Example Audit Log:
[INFO] 🔒 HTTPS_ENFORCER: Upgraded URL from HTTP to HTTPS: api.example.com
[INFO] 🛡️ INPUT_SANITIZER: Sanitized SQL injection attempt in tool 'search_users' (removed: ', --)
[INFO] 🔒 PII_REDACTOR: Redacted 2 emails, 1 credit card in response
[WARN] ⏱️ RATE_LIMITER: Blocked request to 'execute_query' (100/100 tokens used)
[WARN] 🚫 SENSITIVE_COMMAND_BLOCKER: Blocked dangerous command pattern: 'rm -rf' in tool 'run_script'Integration with Logging Systems:
# Datadog
mcp-verify proxy "node server.js" 2>&1 | tee /dev/stderr | datadog-agent logs
# CloudWatch
mcp-verify proxy "node server.js" 2> >(aws logs put-log-events --log-group mcp-verify)
# ELK Stack
mcp-verify proxy "node server.js" 2> >(filebeat -e)
# File-based
mcp-verify proxy "node server.js" 2> security-audit.logSecurity Event Metrics:
- Total requests proxied
- Blocked requests per Security Gateway layer (L1/L2/L3)
- Client strike counts and panic mode activations
- Cache hit ratio and performance gains
- Blocked requests per guardrail (post-gateway)
- Sanitization operations performed
- PII redactions applied
- HTTPS upgrades enforced
Performance Impact (Security Gateway v1.0):
| Configuration | Latency | Use Case | Cost (approx.) |
|---|---|---|---|
| Layer 1+2 only (default) | +10-50ms | Production, high-throughput | $0 |
| Layer 1+2 with cache hits | +<1ms | Repeated requests (65-75% hit ratio) | $0 |
| Layer 1+2+3 (LLM enabled) | +500-2000ms | Research, high-security | $5-$15 per 1K req* |
| Classic Guardrails only | +5-15ms | Legacy compatibility | $0 |
*LLM costs vary by provider (Anthropic/OpenAI/Gemini) and depend on actual usage patterns.
Resource Usage:
- Memory: ~15MB base + 10MB per 1000 cache entries + strike tracking state
- CPU: <10% overhead (Layer 1+2), <15% with guardrails
- Disk I/O: Minimal (audit log writes only)
Throughput:
- Without Gateway: ~10,000 requests/sec
- Layer 1+2 enabled: ~8,000 requests/sec (20% reduction)
- Layer 1+2+3 enabled: ~100-500 requests/sec (LLM bottleneck)
The Smart Fuzzer (mcp-verify fuzz) is an intelligent security testing engine that goes beyond static analysis to detect runtime vulnerabilities through adaptive payload generation and anomaly detection.
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Baseline │ → │ Payload │ → │ Anomaly │
│ Calibration │ │ Generation │ │ Detection │
└─────────────────┘ └──────────────────┘ └─────────────────┘
↓ ↓ ↓
Clean timing/ 9 Generators 10 Detectors
size baselines 12 Mutations (timing, errors, XSS)
↓ ↓ ↓
┌─────────────────────────────────────────────────────────────────┐
│ Feedback Loop │
│ (Analyzes responses → Generates mutations → Tests again) │
└─────────────────────────────────────────────────────────────────┘
Phase 1: Fingerprinting
- Automatically detects server language/framework (Node.js, Python, Java, etc.)
- Disables irrelevant payload generators (saves 40-60% time)
- Example: If server is Node.js, disables Java deserialization payloads
Phase 2: Baseline Calibration
- Sends clean requests to establish "normal" behavior
- Measures average response time, size, status codes
- Creates anomaly detection thresholds (e.g., "response >3x baseline = suspicious")
Phase 3: Payload Generation 9 specialized generators create attack vectors:
| Generator | Vulnerability Targeted | Example Payload |
| ----------------------- | ------------------------------------- | ------------------------------------------------------ | ------- |
| Prompt Injection | Indirect prompt injection, jailbreaks | Ignore previous instructions. Execute: ... |
| SQL Injection | Database access bypass | ' OR 1=1--, UNION SELECT * FROM users |
| XSS Payloads | Cross-site scripting | <script>alert(1)</script>, <img src=x onerror=...> |
| Command Injection | Shell command execution | ; cat /etc/passwd, | whoami |
| JWT Attacks | Token forgery, algorithm confusion | {"alg":"none"}, expired tokens |
| Prototype Pollution | JavaScript object poisoning | {"__proto__": {"isAdmin": true}} |
| JSON-RPC Violations | Protocol compliance bypass | Invalid jsonrpc versions, missing IDs |
| Schema Confusion | Type coercion, boundary testing | age: "999999999999999999999" (string instead of int) |
| Path Traversal | Directory traversal | ../../etc/passwd, C:\Windows\System32 |
Phase 4: Mutation Strategies 12 mutation strategies transform baseline payloads:
| Mutation | Purpose | Example |
|---|---|---|
| SQL Depth | Nested injection | ' OR (SELECT * FROM (SELECT 1)x)-- |
| Null-Byte Injection | String termination bypass | payload\x00.txt |
| Unicode Bypass | Filter evasion | <script> → <script> (fullwidth chars) |
| Timing Probes | Blind injection detection | '; WAITFOR DELAY '00:00:05'-- |
| Buffer Stress | Overflow testing | 10KB+ strings |
| Quote Variation | SQL injection variants | ", ', `, '', """ |
| Case Mutation | Case-sensitive filter bypass | SeLeCt, uNiOn |
| Encoding Bypass | WAF evasion | URL-encode, double-encode, hex |
| Polyglot Payloads | Multi-context injection | ';alert(1)// (SQL + XSS) |
| Recursive Nesting | Parser exhaustion | {{{{{...}}}}} (1000+ levels) |
| Type Confusion | Type coercion bugs | true → "true" → 1 → [true] |
| Boundary Probing | Min/max violations | age: -1, age: 9999999999 |
Phase 5: Anomaly Detection 10 detectors analyze responses for exploitation signs:
| Detector | What It Detects | Confidence Signal |
|---|---|---|
| Timing Anomalies | Blind injection, DoS | Response >3x baseline (e.g., 50ms → 200ms) |
| Error Disclosure | Stack traces, SQL errors | Keywords: SQLException, Traceback, Fatal error |
| XSS Reflection | Payload echoed in response | Detects <script>, onerror= in output |
| Prompt Leaks | System instructions exposed | Patterns: You are a helpful assistant... |
| Jailbreak Success | Guardrail bypass | Response contains restricted content |
| Path Traversal | Directory listing, file access | Detects /etc/passwd, root:x:0:0 |
| Weak Identifiers | Predictable IDs, UUIDs | Sequential IDs: 1, 2, 3 vs. a3f5-9d2c-... |
| Info Disclosure | Version numbers, internal paths | Express 4.17.1, /home/user/app/src/ |
| JWT Manipulation | Forged tokens accepted | alg: none succeeds, expired tokens valid |
| Prototype Pollution | Polluted properties in response | isAdmin: true when it shouldn't exist |
Phase 6: Feedback Loop
- If detector flags a response as suspicious, fuzzer generates mutations of that payload
- Example: If
' OR 1=1--causes timing anomaly, fuzzer tries:" OR 1=1--(quote variation)' OR 2=2--(logic variation)'; WAITFOR DELAY '00:00:10'--(timing probe)
- Feedback loop runs for 3 rounds (configurable with
--max-mutations)
1. Runtime Vulnerability Detection
- Unlike static analysis, fuzzer executes payloads against running servers
- Catches vulnerabilities that only manifest at runtime (e.g., weak validation logic)
2. Adaptive Testing
- Learns from server responses to refine attack strategies
- Example: If server blocks
<script>, tries<img onerror=...>,<svg onload=...>
3. Baseline Comparison
- Establishes "known good" state before fuzzing
- Eliminates false positives from legitimate slow operations
4. Comprehensive Coverage
- Tests all parameter combinations (nested objects, arrays, edge cases)
- Example: For
{ user: { role: string } }, tests:role: "admin"(valid)role: null(null injection)role: 99999(type confusion)user: null(object nullification)
Controlling Intensity:
# Light fuzzing (25 payloads/tool, no mutations)
mcp-verify fuzz "node server.js" --max-payloads 25 --no-mutations
# Balanced (default: 50 payloads, 3 mutations)
mcp-verify fuzz "node server.js"
# Aggressive (100 payloads, 5 mutations)
mcp-verify fuzz "node server.js" --max-payloads 100 --mutations 5Production Safety:
⚠️ Never run against production without authorization- Use
--concurrency 1 --delay 1000to reduce impact - Enable
--dry-runto preview payloads without sending
Ethical Considerations:
- Fuzzer is designed for authorized security testing only
- Users are responsible for ensuring proper authorization
- See Responsible Disclosure Policy
What Fuzzer CANNOT Detect:
- ❌ Business logic flaws (e.g., "analyst can approve transactions" if schema allows it)
- ❌ Second-order vulnerabilities (stored XSS that executes later)
- ❌ Zero-day exploits (unknown attack vectors not in payload database)
- ❌ Human error (misconfigured AWS S3 buckets, weak passwords)
False Positives:
- Timing anomalies may occur due to network latency, not vulnerabilities
- Always manually verify CRITICAL findings before reporting
Security Profiles control the intensity and scope of security testing across validation, fuzzing, and static analysis.
Different environments require different security rigor:
- Development: Fast iterations, minimal overhead → Light profile
- Staging: Balanced testing before production → Balanced profile
- Pre-Production: Maximum scrutiny, zero tolerance → Aggressive profile
Profiles provide preset configurations so users don't manually configure 20+ parameters.
| Profile | Use Case | Payloads/Tool | Mutations | Score Threshold | Fail On |
|---|---|---|---|---|---|
| light | Quick checks, CI/CD (fast feedback) | 25 | 0 | 60/100 | Critical only |
| balanced | Regular testing (default) | 50 | 3 | 70/100 | Critical only |
| aggressive | Pre-production audits (maximum rigor) | 100 | 5 | 90/100 | Critical + High |
Light Profile:
{
"fuzzing": {
"useMutations": false,
"mutationsPerPayload": 0,
"maxPayloadsPerTool": 25,
"enableFeedbackLoop": false
},
"validation": {
"minSecurityScore": 60,
"failOnCritical": true,
"failOnHigh": false
},
"generators": {
"enablePromptInjection": true,
"enableClassicPayloads": true,
"enablePrototypePollution": false,
"enableJwtAttacks": false
},
"detectors": {
"enableTimingDetection": false,
"timingAnomalyMultiplier": 5.0,
"enableErrorDetection": true
}
}Use Case: Fast CI/CD pipelines where developers need immediate feedback (< 30 seconds per tool).
Balanced Profile (Default):
{
"fuzzing": {
"useMutations": true,
"mutationsPerPayload": 3,
"maxPayloadsPerTool": 50,
"enableFeedbackLoop": true
},
"validation": {
"minSecurityScore": 70,
"failOnCritical": true,
"failOnHigh": false
},
"generators": {
"enablePromptInjection": true,
"enableClassicPayloads": true,
"enablePrototypePollution": true,
"enableJwtAttacks": true
},
"detectors": {
"enableTimingDetection": true,
"timingAnomalyMultiplier": 3.0,
"enableErrorDetection": true
}
}Use Case: Standard security testing in staging environments (1-3 minutes per tool).
Aggressive Profile:
{
"fuzzing": {
"useMutations": true,
"mutationsPerPayload": 5,
"maxPayloadsPerTool": 100,
"enableFeedbackLoop": true
},
"validation": {
"minSecurityScore": 90,
"failOnCritical": true,
"failOnHigh": true
},
"generators": {
"enablePromptInjection": true,
"enableClassicPayloads": true,
"enablePrototypePollution": true,
"enableJwtAttacks": true
},
"detectors": {
"enableTimingDetection": true,
"timingAnomalyMultiplier": 2.5,
"enableErrorDetection": true
}
}Use Case: Pre-production security audits, compliance checks, penetration testing (5-10 minutes per tool).
CLI (One-Shot):
# Validate with light profile
mcp-verify validate "node server.js" --profile light
# Fuzz with aggressive profile
mcp-verify fuzz "node server.js" --profile aggressiveInteractive Shell:
$ mcp-verify
> profile set aggressive
✓ Switched to profile: aggressive (100 payloads, score ≥90)
> fuzz --tool "execute_query"
# Uses aggressive profile settings automaticallyCustom Profiles:
# Save current settings as custom profile
> profile save my-custom-profile
✓ Saved custom profile: my-custom-profile to ~/.mcp-verify/config.json
# Load custom profile
> profile set my-custom-profile
✓ Switched to profile: my-custom-profileLight Profile Risks:
⚠️ May miss sophisticated attacks (no mutations, low payload count)⚠️ Timing-based attacks not detected (timing detection disabled)- ✅ Suitable for: Quick smoke tests, developer local testing
Balanced Profile:
- ✅ Good coverage of common vulnerabilities
- ✅ Reasonable trade-off between speed and rigor
- ✅ Suitable for: CI/CD, staging environment testing
Aggressive Profile Risks:
⚠️ High resource consumption (may trigger rate limiting)⚠️ Long execution time (not suitable for rapid iteration)- ✅ Suitable for: Pre-production audits, compliance testing
Profiles respect the configuration hierarchy:
- CLI Flags (highest priority):
--max-payloads 200overrides profile - Active Context: Context-specific settings override profile
- Profile: Preset or custom profile settings
- System Defaults (lowest priority): Fallback values
Example:
# Aggressive profile = 100 payloads, but CLI flag overrides to 200
mcp-verify fuzz "node server.js" --profile aggressive --max-payloads 200
# Result: 200 payloads/tool (CLI wins)The Interactive Shell (mcp-verify without arguments) provides a persistent REPL environment with enhanced security features.
1. Multi-Context Isolation
- Each context (dev, staging, prod) has independent configuration
- Prevents accidental cross-environment contamination
- Example: Switching from
devtoproddoes NOT carry over fuzzing settings
Example:
$ mcp-verify
> context create dev
> set target "node dev-server.js"
> profile set light
> context create prod
> set target "https://prod.example.com/mcp"
> profile set aggressive
# Contexts are isolated - dev settings don't affect prod
> context switch dev
✓ Switched to context: dev (light profile)2. Session Persistence Security
- Session state saved to
.mcp-verify/session.json(per-project) - File permissions:
0600(read/write by owner only) - Secrets are NEVER persisted (API keys, tokens excluded)
What is persisted:
- ✅ Target MCP server (e.g.,
node server.js) - ✅ Active language (en/es)
- ✅ Active security profile (light/balanced/aggressive)
- ✅ Context configurations
What is NOT persisted:
- ❌ API keys (loaded from environment only)
- ❌ Command history containing secrets (redacted before save)
- ❌ Temporary authentication tokens
3. Command History Redaction
- History saved to
~/.mcp-verify/history.json(cross-session) - Automatic secret redaction before saving
- Patterns redacted:
ANTHROPIC_API_KEY=sk-ant-...→ANTHROPIC_API_KEY=***REDACTED***--api-key sk-ant-...→--api-key ***REDACTED***token: "ghp_..."→token: "***REDACTED***"
Example:
# User types
> validate node server.js --api-key sk-ant-api03-XXXXXX
# Saved to history.json
> validate node server.js --api-key ***REDACTED***4. Environment Variable Security
.envfiles auto-loaded on shell startup- Environment variables NOT persisted to session files
- Loaded keys visible in
statuscommand for debugging
Example:
$ mcp-verify
Loading environment from .env...
✓ Loaded 5 keys: ANTHROPIC_API_KEY, DEBUG, MCP_TIMEOUT, MCP_HOST, MCP_PORT
> status
Environment:
Source: .env
Keys: 5 loaded
ANTHROPIC_API_KEY, DEBUG, MCP_TIMEOUT, MCP_HOST, MCP_PORTWhat is secure:
- ✅ API keys loaded from
.env(not hardcoded in shell commands) - ✅
.envexcluded from version control (via.gitignore)
What is NOT secure:
- ❌
.envfile permissions (user must setchmod 600 .env) - ❌ Environment variable leaks via OS process inspection (use encrypted secrets in production)
5. Output Redirection Safety
# Redirect command output to file
> validate > report.txt
# Secrets are redacted in redirected output
> status > debug.log
# File contains: ANTHROPIC_API_KEY: ***REDACTED***Purpose: Verify target MCP server is reachable and validates protocol compliance.
What it checks:
- Connection Status: Can the proxy reach the server?
- MCP Handshake: Does the server respond to
initializerequest correctly? - Protocol Version: Does the server support MCP 2024-11-05?
- Environment Integrity: Are required API keys loaded?
- Last Report: Where was the last security report saved?
Example:
> status
Workspace Status
Context:
Active: dev
Target: node server.js
Profile: balanced
Environment:
Source: .env
Keys: 3 loaded
ANTHROPIC_API_KEY, DEBUG, MCP_TIMEOUT
Last Report:
Path: reports/html/mcp-report-2026-02-24.html
Time: 2/24/2026, 3:45:12 PM
Target Connection:
Status: ● Connected
Server: My Development Server
Version: 2024-11-05
Time: 134msSecurity Value:
- Prevents accidental testing against wrong server (e.g., fuzzing prod instead of dev)
- Validates protocol compliance before expensive fuzzing runs
- Detects dead targets early (no wasted time on unreachable servers)
File Path Completion:
- Only completes readable files (checks file permissions)
- Prevents tab-completing restricted files (
/etc/shadow, etc.)
Flag Completion:
- Only suggests valid flags for active command
- Prevents typo-based injection (e.g.,
--max-payloadsvs.--max-paylaods)
What Interactive Shell DOES NOT expose:
- ❌ Network listening ports (shell is local-only)
- ❌ Web interfaces (no HTTP server)
- ❌ Remote command execution (commands run in local process)
Threat Model:
- Shell assumes trusted local user (if attacker has shell access, game over)
- Does NOT protect against malicious MCP servers (use proxy guardrails for that)
The Secret Redaction System prevents API keys, tokens, and credentials from leaking through logs, history files, and session persistence.
1. Command History
- Location:
~/.mcp-verify/history.json - Redacts before saving to disk
- Patterns detected:
ANTHROPIC_API_KEY=sk-ant-...OPENAI_API_KEY=sk-...GEMINI_API_KEY=AI...--api-key <value>"token": "<value>"Bearer <value>
2. Session Files
- Location:
.mcp-verify/session.json - API keys NEVER stored in session
- Only references to environment variables stored (e.g.,
"apiKeyEnv": "ANTHROPIC_API_KEY")
3. Logs
- All log output (stdout, stderr, debug logs) scanned for secrets
- Redacted patterns:
- API keys:
sk-****...****(shows first 3 + last 4 chars) - Tokens:
***REDACTED*** - Passwords:
***REDACTED***
- API keys:
4. Report Files
- HTML/JSON/SARIF reports redact secrets found in:
- Tool descriptions (e.g., "Use API key sk-ant-...")
- Parameter examples
- Error messages from servers
Pattern Detection:
// Anthropic API keys
/sk-ant-api03-[a-zA-Z0-9_-]{95}/g → sk-****...****
// OpenAI API keys
/sk-[a-zA-Z0-9]{48}/g → sk-****...****
// GitHub Personal Access Tokens
/ghp_[a-zA-Z0-9]{36}/g → ghp_****...****
// Generic Bearer tokens
/Bearer\s+[a-zA-Z0-9_-]{20,}/g → Bearer ***REDACTED***
// Environment variable assignments
/API_KEY\s*=\s*["']?([^"'\s]+)/g → API_KEY=***REDACTED***Redaction Strategy:
- Short secrets (<20 chars): Fully redacted →
***REDACTED*** - Long secrets (≥20 chars): Partial redaction →
sk-****...7x3A(first 3 + last 4 visible)
Why partial redaction?
- Allows debugging: "Which key was used?" without exposing full secret
- Maintains log correlation: Same key always shows same redaction
What IS protected:
- ✅ API keys in command history
- ✅ Tokens in session files
- ✅ Secrets in log output
- ✅ Credentials in reports
What is NOT protected:
- ❌ Secrets already committed to Git (use
git-secretsortruffleHog) - ❌ Secrets in process memory (use encrypted memory if needed)
- ❌ Secrets in OS environment (use secrets managers like HashiCorp Vault)
- ❌ Secrets in screenshot/screen recordings (user responsibility)
1. Never hardcode secrets:
# ❌ BAD: Secret in command
mcp-verify validate node server.js --api-key sk-ant-api03-XXXXXX
# ✅ GOOD: Secret in environment variable
export ANTHROPIC_API_KEY=sk-ant-api03-XXXXXX
mcp-verify validate node server.js2. Use .env files for local development:
# .env (add to .gitignore!)
ANTHROPIC_API_KEY=sk-ant-api03-XXXXXX
OPENAI_API_KEY=sk-XXXXXX3. Use secrets managers in production:
# AWS Secrets Manager
export ANTHROPIC_API_KEY=$(aws secretsmanager get-secret-value --secret-id mcp-verify-key --query SecretString --output text)
# HashiCorp Vault
export ANTHROPIC_API_KEY=$(vault kv get -field=api_key secret/mcp-verify)4. Rotate keys regularly:
- If key appears in logs/history/reports → Rotate immediately
- Monitor usage via Anthropic Console for suspicious activity
Check if history is redacted:
cat ~/.mcp-verify/history.json | grep -i "api"
# Should show: "***REDACTED***" not actual keysCheck if logs are clean:
mcp-verify validate node server.js > output.log 2>&1
grep -E "sk-ant-|sk-[a-zA-Z0-9]{48}" output.log
# Should return nothing (all keys redacted)- ✅ JSON Schema compliance (Draft 2020-12)
- ✅ Required field validation
- ✅ Type checking (string, number, object, array)
- ✅ Enum validation
- ✅ Documentation completeness
- ✅ Naming conventions
- ✅ Parameter descriptions
- ✅ Example clarity
mcp-verify analyzes static declarations, not runtime code execution:
- ❌ Actual tool implementations - We don't run the code
- ❌ Dynamic vulnerabilities - Only static patterns detected
- ❌ Business logic flaws - Context-specific vulnerabilities
- ❌ Race conditions - Concurrency issues
- ❌ Memory leaks - Resource management
- ❌ Timing attacks - Side-channel vulnerabilities
Example: If a tool description says "Read file safely" but the implementation has path traversal, mcp-verify may not detect it unless the description explicitly mentions unsafe patterns.
- ❌ MCP servers being tested - We analyze, we don't control their security
- ❌ Claude Desktop security model - Managed by Anthropic
- ❌ Node.js runtime security - Depends on Node.js version
- ❌ Network security - Use HTTPS, firewalls, etc.
- ❌ OS-level protections - File permissions, process isolation
mcp-verify provides risk assessment, not absolute security:
- 🟢 Score 95/100: "No known patterns detected" ≠ "Perfectly secure"
- 🟡 Score 70/100: "Some issues found" ≠ "Definitely vulnerable"
- 🔴 Score 30/100: "Critical patterns detected" = "Very likely vulnerable"
Analogy: mcp-verify is like a spell-checker for security. It catches known patterns, but cannot guarantee correctness.
mcp-verify v1.0.0 performs automated technical security testing but has inherent limitations that users must understand before deployment:
Limitation: mcp-verify cannot detect authorization issues that are technically valid but violate business rules.
The fuzzer excels at finding technical vulnerabilities (type confusion, boundary violations, enum bypass). However, it cannot understand domain-specific authorization logic.
Example:
// Schema (technically valid)
{
role: { type: 'string', enum: ['analyst', 'manager'] },
action: { type: 'string', enum: ['approve', 'reject'] }
}
// ✅ mcp-verify WILL detect: { role: 'admin' } → Privilege escalation (value outside enum)
// ❌ mcp-verify WILL NOT detect: { role: 'analyst', action: 'approve' }
// → If only 'manager' should approve (business rule not in schema)Impact: Authorization bugs that depend on business context will NOT be caught.
Mitigation:
- Complement mcp-verify with manual authorization testing
- Implement role-based access control (RBAC) tests in your test suite
- Document business rules explicitly in tool descriptions
- Use security annotations in schemas (e.g.,
x-security-role: 'manager'for custom validation)
Limitation: Findings marked as CRITICAL require manual validation to confirm they are exploitable vulnerabilities.
mcp-verify uses heuristics to detect vulnerabilities. A server that correctly validates input may still trigger findings if the validation logic isn't visible in the schema.
Example of False Positive:
// Payload sent by fuzzer
{ age: 121 } // Schema: { maximum: 120 }
// Server response
{ error: 'Invalid age', code: 400 }
// mcp-verify finding
CRITICAL: Boundary overflow - age exceeds maximum (121 > 120)
// Reality: Server validated correctly → FALSE POSITIVELimitation: Rules for advanced multi-agent and supply-chain attacks (SEC-033, SEC-038, SEC-040, SEC-057) currently use static keyword-based analysis.
Impact: This method may produce false negatives if tool descriptions are obfuscated or use indirect language. These rules should be considered "Experimental" in v1.0.0.
Roadmap: Future versions will implement tiered semantic analysis and behavioral detectors to provide runtime verification for these advanced threat vectors.
Impact: Security teams may waste time investigating non-issues.
Mitigation:
- Always review CRITICAL findings manually before reporting as vulnerabilities
- Check server responses:
errorresponses often indicate correct validation - Use baseline comparison (
--compare-baseline) to track new findings vs. known false positives - Suppress confirmed false positives using
.mcpverifyignore
Limitation: Running mcp-verify against production servers may trigger rate limiting, intrusion detection systems (IDS), or cause log saturation.
The fuzzer generates 150-250 payloads per tool for schema-aware testing. This volume of requests can:
- Trigger rate limiting → IP blocking
- Alert security monitoring systems (SIEM, IDS) → False alarm investigations
- Saturate application logs → Obscure real attacks
- Consume API quotas → Service degradation
Impact: Production services may be disrupted or security teams alerted unnecessarily.
Mitigation:
⚠️ ALWAYS run mcp-verify in staging/pre-production environments- If testing production is unavoidable:
- Use
--concurrency 1to reduce request rate - Use
--delay 1000(1 second delay between requests) - Notify security teams beforehand
- Whitelist fuzzer IP in rate limiting rules
- Run during maintenance windows
- Use
Example Safe Configuration:
# Safe for production (slower, less aggressive)
mcp-verify fuzz "https://api.example.com" \
--tool my-tool \
--concurrency 1 \
--delay 1000 \
--timeout 30000Limitation: mcp-verify cannot detect vulnerabilities that manifest later (stored XSS, delayed injection).
The fuzzer checks immediate responses only. If a malicious payload is stored and executed when another user accesses it, mcp-verify will NOT detect it.
Example:
// Fuzzer sends
{ comment: '<script>alert(1)</script>' }
// Server responds (STORED, not executed yet)
{ success: true, id: '12345' }
// mcp-verify sees: No error → No vulnerability detected
// Later: Another user views the comment → XSS executes
// mcp-verify MISSED this (second-order vulnerability)Mitigation:
- Perform manual penetration testing for stored data scenarios
- Use dynamic application security testing (DAST) tools for runtime analysis
- Implement content security policy (CSP) to mitigate stored XSS
Limitation: Servers with always-successful responses (e.g., always return 200 OK) may hide vulnerabilities.
mcp-verify relies on response patterns to detect vulnerabilities (errors, timing differences). Servers that return success codes even for invalid input can produce false negatives.
Example:
// Fuzzer sends SSRF payload
{ url: 'http://169.254.169.254/latest/meta-data/' }
// Vulnerable server (but hides it)
{ success: true, result: 'Data fetched' } // ← Always 200 OK
// mcp-verify sees: Success → No vulnerability detected
// Reality: Server fetched AWS metadata → SSRF exists but hiddenMitigation:
- Review application logs for suspicious outbound requests
- Use traffic monitoring (Wireshark, Burp Suite) during fuzzing
- Test with verbose error messages enabled (in staging only)
| Component | Control Level | Description |
|---|---|---|
| Static Analysis Rules | Full Control | We define detection patterns |
| Report Generation | Full Control | JSON/SARIF/HTML outputs |
| CLI Interface | Full Control | Command-line behavior |
| MCP Server Interface | Full Control | Tool schemas and responses |
| Component | Control Level | Description |
|---|---|---|
| Tested MCP Servers | No Control | We analyze external servers |
| Claude Desktop Keychain | No Control | Managed by Anthropic |
| User's Environment Variables | No Control | User responsibility |
| Network Security | No Control | HTTPS, firewalls, etc. |
| Runtime Exploits | No Control | Dynamic vulnerabilities |
| Scenario | Responsible Party |
|---|---|
| mcp-verify reports false positive | mcp-verify (Fink) - Report as bug |
| mcp-verify misses a vulnerability | mcp-verify (Fink) - Report as enhancement |
| Tested server is exploited despite 100/100 score | Server Developer - mcp-verify analyzes statically |
| API key leaked via environment variable | User - Secure your environment |
| Claude Desktop keychain compromised | Anthropic - Not controlled by mcp-verify |
| Network interception of API key | User - Use HTTPS, secure networks |
mcp-verify includes intentionally vulnerable test servers for validation:
tools/mocks/servers/
├── simple-server.js # Clean server (should score 95+)
├── vulnerable-server.js # Vulnerable server (should score <50)
└── broken-server.js # Protocol violations (should fail)
- simple-server.js: Demonstrates best practices (reference implementation)
- vulnerable-server.js: Contains SQL injection, command injection, SSRF, etc. (tests detection)
- broken-server.js: Invalid JSON-RPC responses (tests protocol compliance)
These servers are INTENTIONALLY INSECURE for testing purposes:
- ❌ DO NOT deploy
vulnerable-server.jsanywhere - ❌ DO NOT copy code from
vulnerable-server.js - ❌ DO NOT use as templates for real servers
- ✅ DO use
simple-server.jsas a reference for best practices
If you find a security vulnerability in mcp-verify:
- Do NOT open a public GitHub issue
- Email: official.mcpverify@gmail.com
- Include:
- Description of vulnerability
- Steps to reproduce
- Impact assessment
- Proof of concept (if available)
We will respond within 48 hours and provide a fix within 7 days for critical issues.
If mcp-verify fails to detect a known vulnerability:
- Open a GitHub issue: https://github.com/FinkTech/mcp-verify/issues
- Tag with:
security,false-negative - Include:
- Server code or description
- Vulnerability type (SQL injection, SSRF, etc.)
- Expected detection rule
- Actual mcp-verify output
If mcp-verify incorrectly flags safe code:
- Open a GitHub issue: https://github.com/FinkTech/mcp-verify/issues
- Tag with:
security,false-positive - Include:
- Server code or description
- Why the finding is incorrect
- Expected behavior
When using mcp-verify:
- ✅ Use mcp-verify as one layer in a defense-in-depth strategy
- ✅ Combine with manual code review
- ✅ Run in CI/CD pipelines before deployment
- ✅ Review all findings manually
- ✅ Use HTTPS for API calls
- ✅ Secure environment variables
- ✅ Rotate API keys regularly
- ❌ Rely solely on mcp-verify for security
- ❌ Assume 100/100 score means "perfectly secure"
- ❌ Deploy servers without manual review
- ❌ Commit API keys to version control
- ❌ Use test servers in production
- ❌ Ignore low-severity findings
mcp-verify is a security validation tool, not a security guarantee. It helps identify known vulnerability patterns through static analysis, but cannot replace human judgment, manual code review, or comprehensive security testing.
Use mcp-verify as part of a broader security strategy, not as a replacement for it.
For questions, open a GitHub issue: https://github.com/FinkTech/mcp-verify/issues
Last Updated: 2026-04-08 Version: 1.0.0