gemsec-security-analyzer-mcp
v1.3.0
Published
MCP server for security analysis of NextJS/React TypeScript codebases
Maintainers
Readme
GemSec MCP Server
GemSec is a Model Context Protocol (MCP) tool that scans JavaScript/TypeScript codebases (Next.js/React friendly) for common security pitfalls, then produces actionable CLI and HTML reports with contextual snippets, debugging prompts, and deep links.
🚀 Quick Start
Configure in Cursor (Settings → Features → MCP Servers):
"gemsec-local": {
"command": "npx",
"args": [
"-y",
"gemsec-security-analyzer-mcp@latest"
]
},That's it! No cloning, no building, no manual setup. Just install and use.
Key Features
- Recursive Analysis – Scan an individual file or an entire directory (skipping
node_modules, dot folders, and non JS/TS extensions). - Rule Library – Detects XSS risks, missing CSRF tokens, hardcoded secrets, SQL injection patterns, weak crypto, insecure storage, and more (see
src/config/securityPatterns.ts). - Rich Reporting
- Terminal output with severity badges, code snippets, VS Code deep links, and suggested prompts for debugging.
- Styled HTML report (
reports/security-report-*/index.html) generated inside the root of the analyzed project.
- Best Practices Helper – Quick reference guide for hardened Next.js/React deployments.
Security Patterns
GemSec scans for 14 common security vulnerabilities in JavaScript/TypeScript codebases. Each pattern is detected using regex-based matching and includes severity classification, detailed explanations, and actionable recommendations.
Pattern Overview
| Pattern Name | Severity | Description |
|--------------|----------|-------------|
| XSS - eval() | 🔴 Critical | Detects use of eval() which can execute arbitrary code |
| Hardcoded Secrets | 🔴 Critical | Finds API keys, passwords, secrets, or tokens hardcoded in source code |
| SQL Injection Risk | 🔴 Critical | Identifies SQL queries with string interpolation vulnerabilities |
| XSS - dangerouslySetInnerHTML | 🟠 High | Detects unsafe HTML injection in React components |
| Missing Input Validation | 🟠 High | Flags unvalidated user input from request parameters |
| CORS Misconfiguration | 🟠 High | Finds CORS configured to accept all origins (*) |
| Missing CSRF Protection | 🟠 High | Detects forms without CSRF token protection |
| Weak Crypto | 🟠 High | Identifies use of weak hashing algorithms (MD5/SHA1) |
| Insecure localStorage | 🟡 Medium | Flags use of localStorage for sensitive data storage |
| Insecure Random | 🟡 Medium | Detects use of Math.random() for security-sensitive operations |
| Insecure HTTP | 🟡 Medium | Finds HTTP URLs (should use HTTPS) |
| Missing Security Headers | 🟡 Medium | Checks for missing CSP, X-Frame-Options, or HSTS headers |
| Unsafe Redirect | 🟡 Medium | Detects redirects without URL validation |
| Exposed Server Info | 🔵 Low | Finds X-Powered-By headers that expose server technology |
Detailed Pattern Descriptions
🔴 Critical Severity
XSS - eval()
- Issue:
eval()executes strings as JavaScript code, allowing arbitrary code execution - Risk: Remote code execution in browser or server if input comes from untrusted sources
- Recommendation: Avoid
eval(), useJSON.parse()or safer alternatives
Hardcoded Secrets
- Issue: API keys, passwords, secrets, or tokens hardcoded in source code
- Risk: Secrets exposed in repositories, build artifacts, or shared code can be used by attackers to access APIs, databases, or services
- Recommendation: Use environment variables (
.env) and never commit secrets to version control
SQL Injection Risk
- Issue: SQL queries constructed with string interpolation instead of parameterized queries
- Risk: Attackers can inject SQL code to read sensitive data, modify data, or gain remote code execution on the database server
- Recommendation: Use parameterized queries or ORM like Prisma
🟠 High Severity
XSS - dangerouslySetInnerHTML
- Issue: React's
dangerouslySetInnerHTMLallows direct HTML injection into DOM without sanitization - Risk: If content comes from user input or untrusted sources, attackers can inject malicious scripts leading to cookie theft, session hijacking, or defacement
- Recommendation: Use DOMPurify for HTML sanitization or avoid innerHTML entirely
Missing Input Validation
- Issue: User input from
req.body,req.query, orreq.paramsused without validation - Risk: Can lead to injection attacks (SQL, NoSQL, Command), type confusion, buffer overflow, or business logic bypass
- Recommendation: Validate all input using libraries like
zodorjoi
CORS Misconfiguration
- Issue: CORS configured to accept all origins (
*) - Risk: Any website can make authenticated requests to your API, enabling CSRF attacks or unauthorized data access
- Recommendation: Restrict CORS to trusted domains only
Missing CSRF Protection
- Issue: Forms without CSRF token protection
- Risk: Attackers can make requests on behalf of authenticated users, performing actions like changing passwords or deleting data without user knowledge
- Recommendation: Implement CSRF tokens for all form mutations
Weak Crypto
- Issue: Use of weak hashing algorithms (MD5/SHA1)
- Risk: Vulnerable to collision attacks and too fast for password hashing; attackers can use rainbow tables or brute force
- Recommendation: Use bcrypt, scrypt, or Argon2 for password hashing
🟡 Medium Severity
Insecure localStorage
- Issue: Use of
localStoragefor storing sensitive data - Risk: Accessible by JavaScript in same origin (including XSS attacks); no httpOnly protection; tokens can be stolen if XSS occurs
- Recommendation: Do not store tokens or sensitive data in localStorage; use httpOnly cookies instead
Insecure Random
- Issue: Use of
Math.random()for security-sensitive operations - Risk: Pseudo-random number generator is predictable; attackers can guess or predict generated values for tokens, session IDs, or cryptographic keys
- Recommendation: Use
crypto.randomBytes()orcrypto.getRandomValues()for security
Insecure HTTP
- Issue: HTTP URLs used instead of HTTPS
- Risk: Data sent in plain text; attackers on same network can intercept, read, and modify communications including credentials and tokens
- Recommendation: Use HTTPS for all communications
Missing Security Headers
- Issue: Important security headers (CSP, X-Frame-Options, HSTS) may not be configured
- Risk: More vulnerable to XSS, clickjacking, and man-in-the-middle attacks
- Recommendation: Implement CSP, X-Frame-Options, and HSTS headers
Unsafe Redirect
- Issue: Redirects without URL validation
- Risk: Attackers can redirect users to malicious websites for phishing or to bypass security controls
- Recommendation: Validate redirect URLs against a whitelist of domains
🔵 Low Severity
Exposed Server Info
- Issue: X-Powered-By header exposes server technology information
- Risk: Helps attackers find technology-specific vulnerabilities during reconnaissance
- Recommendation: Disable X-Powered-By header to minimize information disclosure
Extending Security Patterns
To add new security patterns, edit src/config/securityPatterns.ts:
{
name: "Your Pattern Name",
pattern: /your-regex-pattern/g,
severity: "high" | "medium" | "low" | "critical",
message: "Brief description of the issue",
recommendation: "Actionable fix suggestion",
explanation: "Detailed explanation of the vulnerability and its risks"
}Available MCP Tools
| Tool Name | Description | Arguments |
|-----------|-------------|-----------|
| analyze_file | Scan a single file | { "file_path": "/abs/path/to/file.tsx", "file_content"?: "..." } |
| analyze_directory | Recursively scan a folder for JS/TS files | { "directory_path": "/abs/path/to/project", "files"?: [...] } |
| get_security_best_practices | Returns the curated best-practices checklist | none |
Note: For remote MCP servers, you can provide file_content (for analyze_file) or files array (for analyze_directory) to send file contents directly instead of reading from filesystem.
Typical Workflow
Start the MCP server
- StdIO/CLI
node ./build/index.js - Express + SSE backend
npm start # or: PORT=4000 node ./build/httpServer.js
- StdIO/CLI
Call
analyze_directoryFor local server:
{ "name": "analyze_directory", "arguments": { "directory_path": "/Users/me/projects/test-project" } }For remote server (with file contents):
{ "name": "analyze_directory", "arguments": { "directory_path": "/Users/me/projects/test-project", "files": [ { "path": "/Users/me/projects/test-project/src/App.tsx", "content": "// file content here..." } ] } }Open the HTML report
- GemSec determines the project root (presence of
package.json,.git,pnpm-workspace.yaml, oryarn.lock) and writes the report under<project>/reports/security-report-*/. - The CLI response includes the exact
index.htmlpath plus embedded VS Code links for each finding.
- GemSec determines the project root (presence of
IDE / Cursor Integration
GemSec is an MCP server, so any MCP-aware IDE (such as Cursor) can invoke it directly.
Setup with StdIO (Manual/Development)
Register the server in Cursor
Open
Settings → Features → MCP Servers.Add a new custom server with the following configuration:
If installed globally:
{ "mcpServers": { "gemsec": { "command": "gemsec-mcp" } } }If installed locally:
{ "mcpServers": { "gemsec": { "command": "npx", "args": ["gemsec-mcp"] } } }Or with full path (if needed):
{ "mcpServers": { "gemsec": { "command": "node", "args": ["/absolute/path/to/node_modules/gemsec-mcp/build/index.js"] } } }Name:
gemsecSave; restart Cursor if prompted.
Prompting the assistant
- Mention GemSec or the tool you want, e.g. "Run GemSec
analyze_directoryonsrc/features/log-activity" or "Use GemSec to analyzeFormAddNewUser.tsx." - Cursor will surface the GemSec tools (
analyze_file,analyze_directory,get_security_best_practices) in the tool picker.
- Mention GemSec or the tool you want, e.g. "Run GemSec
Handling results
- The response includes a Markdown summary plus the HTML path. Open the
index.htmlinside your project (e.g.…/reports/security-report-*/index.html). - Use the VS Code
vscode://file/...links embedded in the text or HTML report to jump straight to the relevant lines.
- The response includes a Markdown summary plus the HTML path. Open the
Setup with SSE (Server-Sent Events)
To use SSE transport, you need to run the HTTP server first, then connect from the client using HTTP endpoints.
1. Running HTTP Server
# Build the project first
npm run build
# Run HTTP server (default port 3030)
npm start
# Or with a custom port
PORT=8080 npm startServer will run at http://localhost:3030 (or the port you specify).
2. Verify Server is Running
# Test health endpoint
curl http://localhost:3030/healthz
# Response:
# {
# "status": "ok",
# "name": "GemSec",
# "transport": "sse"
# }3. Connect with Streamable HTTP Client
Streamable HTTP transport uses a single endpoint that handles both GET and POST:
- GET/POST
/mcp- Primary endpoint (modern standard)- GET: Opens SSE stream (server → client)
- POST: Sends requests to server (client → server)
- Legacy endpoints (for backward compatibility):
- GET/POST
/sse- Legacy SSE endpoint - POST
/messages- Legacy message endpoint
- GET/POST
Example using curl for testing:
# 1. Open SSE connection (will get sessionId from response headers)
curl -N http://localhost:3030/sse
# 2. In another terminal, send message (replace <sessionId> with ID from step 1)
curl -X POST http://localhost:3030/messages?sessionId=<sessionId> \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}'4. Configuration for Web-based MCP Clients
If you are using a web-based MCP client that supports SSE, the configuration is usually like this:
{
"mcpServers": {
"gemsec-streamable": {
"type": "streamable-http",
"url": "http://localhost:3030/mcp"
}
}
}For remote deployment (with authentication):
{
"mcpServers": {
"gemsec-remote": {
"type": "streamable-http",
"url": "https://your-server.com/mcp",
"headers": {
"Authorization": "Bearer your-secret-token"
}
}
}
}For legacy SSE clients:
{
"mcpServers": {
"gemsec-sse": {
"url": "http://localhost:3030/sse",
"transport": "sse"
}
}
}For legacy SSE with authentication:
{
"mcpServers": {
"gemsec-sse": {
"url": "http://localhost:3030/sse",
"transport": "sse",
"headers": {
"Authorization": "Bearer your-secret-token"
}
}
}
}Note: Cursor currently works better with StdIO transport. SSE transport is more suitable for:
- Web-based MCP clients
- Remote deployments (Azure, Docker, etc.)
- Integration with HTTP-based tools
- Multi-client scenarios
Development Guide
- Source Layout
src/gemsecServer.ts– MCP server bootstrap (registers tools, handles routing, resolves project roots).src/index.ts– StdIO entry point for CLI-oriented MCP clients.src/httpServer.ts– Express + SSE transport so HTTP clients can connect over/sseand/messages.src/services/securityAnalyzer.ts– Core scanner; reads files and applies regex-based rules defined insrc/config/securityPatterns.ts.src/reporters/*.ts– Text and HTML reporting pipelines.src/types.ts– Shared interfaces (SecurityIssue,SecurityPattern, etc.).
- Commands
npm run build– Compile TypeScript tobuild/.npm run dev– TypeScript watch mode.
- Extending Rules
- Add new entries to
securityPatterns.ts. - Each pattern requires a name, regex, severity, message, and recommendation.
- Add new entries to
- Adding Tools
- Update
registerListToolsHandler+registerCallToolHandlerinsrc/gemsecServer.ts.
- Update
Express + SSE Backend
The HTTP transport aligns with the Azure-ready blueprint described by Build5Nines for deploying TypeScript-based MCP servers with Express, Docker, and Azure Developer CLI [source].
📖 Complete SSE setup documentation: See SSE_SETUP.md for detailed guide.
Endpoints
GET/POST /mcp– Primary Streamable HTTP endpoint (modern standard). Handles both SSE streams (GET) and requests (POST) on a single endpoint.GET/POST /sse– Legacy SSE endpoint (for backward compatibility).POST /messages– Legacy message endpoint (for backward compatibility).GET /healthz– Lightweight readiness probe that reports tool name and transport type.
Running locally
npm start # defaults to port 3030 PORT=8080 npm start # override the portQuick Test
# Test health endpoint curl http://localhost:3030/healthz # Expected: {"status":"ok","name":"GemSec","transport":"sse"}Authentication (Optional)
- By default, the server runs without authentication (suitable for local development)
- To enable token-based authentication, set the
GEMSEC_AUTH_TOKENenvironment variable:GEMSEC_AUTH_TOKEN=your-secret-token npm start - Clients must include the token in requests:
- Header:
Authorization: Bearer your-secret-token - Or query parameter:
?token=your-secret-token
- Header:
- Note: "No stored tokens found" messages from the client are normal when authentication is disabled
Deployment hints
- Expose the same port you pass through
$PORTand ensure your ingress preserves SSE headers. - When scaling beyond a single replica (e.g., multiple Azure Container App pods), configure session affinity so
/messagesrequests reach the pod that owns the/ssestream. - The included
Dockerfilealready produces a minimal Node 20 runtime suitable for ACA or other container targets. - For production deployments, consider enabling authentication via
GEMSEC_AUTH_TOKEN. - See DEPLOYMENT.md for complete Docker deployment guide.
- Expose the same port you pass through
Output Anatomy
Each finding contains:
- Severity & Type – e.g.,
HIGH — Missing CSRF Protection. - Line + Code – Source line and the specific snippet.
- Context Block – Multi-line excerpt with highlighting for quick review.
- Recommendation – Suggested remediation.
- Debug Prompt – Plain-text instruction suitable for AI pair programming or issue tracking.
- VS Code Deep Link –
vscode://file/<path>:<line>to jump directly into the file.
Auto-Open Browser
When an HTML report is generated, it will automatically open in your default browser. This feature works on:
- macOS: Uses
opencommand - Windows: Uses
startcommand - Linux: Uses
xdg-opencommand
To disable auto-open, set the environment variable:
GEMSEC_NO_AUTO_OPEN=true npm startIf auto-open fails (e.g., no browser available), the report path will still be shown in the response, and you can open it manually.
Troubleshooting
- Report path not under your repo? Ensure the analyzed directory contains a project marker (
package.json,.git, etc.). GemSec walks up the filesystem until it finds one. - Missing findings? Only
.ts,.tsx,.js, and.jsxfiles are scanned. - Custom report locations? Pass an absolute path via
analyze_directoryoranalyze_file; GemSec will infer the nearest project root automatically.
Remote MCP Server Support
⚠️ Important: When using a remote MCP server (deployed in the cloud), the server cannot access files on your local filesystem by default. However, GemSec now supports sending file contents directly to the remote server!
Option 1: Send File Content Directly (Recommended for Remote Servers)
You can provide file contents directly in the tool call, allowing remote servers to analyze your local files:
For single file analysis:
{
"name": "analyze_file",
"arguments": {
"file_path": "/Users/me/project/src/App.tsx",
"file_content": "import React from 'react';\n\nexport default function App() {\n return <div>Hello</div>;\n}"
}
}For directory analysis:
{
"name": "analyze_directory",
"arguments": {
"directory_path": "/Users/me/project/src",
"files": [
{
"path": "/Users/me/project/src/App.tsx",
"content": "import React from 'react';\n\nexport default function App() {\n return <div>Hello</div>;\n}"
},
{
"path": "/Users/me/project/src/components/Button.tsx",
"content": "export function Button() { return <button>Click</button>; }"
}
]
}
}How it works:
- The client reads files from the local filesystem
- File contents are sent to the remote server in the tool call
- The remote server analyzes the content without needing filesystem access
- Results are returned with the original file paths for proper reporting
Option 2: Use Local MCP Server (Recommended for Local Development)
For local development, use StdIO transport with a local server:
{
"mcpServers": {
"gemsec": {
"command": "node",
"args": ["/absolute/path/to/security-analyzer-mcp/build/index.js"]
}
}
}Option 3: Analyze Files on Remote Server
If your files are already on the remote server's filesystem, you can use the remote server directly with file paths:
{
"name": "analyze_directory",
"arguments": {
"directory_path": "/remote/path/to/project"
}
}Best Practice:
- Use Option 1 (file content) when you want to analyze local files with a remote server
- Use Option 2 (local server) for local development (most efficient)
- Use Option 3 (remote paths) when files are already on the remote server
Using mcp-remote with GemSec
If you're using npx mcp-remote to connect to a remote GemSec server (like in the PostHog example), here's what you need to know:
Configuration example:
{
"mcpServers": {
"gemsec-remote": {
"command": "npx",
"args": [
"-y",
"mcp-remote@latest",
"https://your-gemsec-server.com/sse",
"--header",
"Authorization:Bearer your-token"
],
"env": {
"GEMSEC_AUTH_TOKEN": "your-token"
}
}
}
}How it works:
mcp-remoteacts as a proxy between your local client and the remote GemSec server- When you call
analyze_fileoranalyze_directory, the AI assistant (Cursor) should read the local files and include their content in the tool call - The remote server receives the file contents and can analyze them without needing filesystem access
Important Notes:
- ✅ Works with smart AI assistants: If your AI assistant (like Cursor) can read local files and include
file_content/filesin tool calls, this will work seamlessly - ⚠️ Manual file reading: If the AI doesn't automatically read files, you may need to explicitly provide file contents
- 💡 Recommendation: For local development, use a local StdIO server instead. Use
mcp-remotewhen you want to leverage cloud resources or when analyzing files that are already on the remote server
Example tool call that works with mcp-remote:
{
"name": "analyze_file",
"arguments": {
"file_path": "/Users/me/project/src/App.tsx",
"file_content": "// AI assistant reads this from local file and includes it"
}
}License
MIT – see LICENSE (or add one if missing).
