LSP (Language Server Protocol) Integration¶
Version: 1.0 Last updated: 2026-01-26
Introduction¶
LSP integration allows mcp-scan to communicate with language servers like Pyright (Python), typescript-language-server (TypeScript/JavaScript), and gopls (Go). This provides:
- Type-aware analysis: Detection based on real types, not just patterns
- Precise call graphs: Building call graphs using server information
- Symbol resolution: Precise tracking of definitions and references
- Dangerous type detection: Identification of
Any,object,unknown
Architecture¶
+---------------------------------------------------------------------+
| mcp-scan |
| +---------------------------------------------------------------+ |
| | LSP Manager | |
| | +-------------+ +-------------+ +-------------+ | |
| | | Python | | TypeScript | | Go | | |
| | | Client | | Client | | Client | | |
| | +------+------+ +------+------+ +------+------+ | |
| +---------+---------------+---------------+-------------------------+
+------------+---------------+---------------+--------------------------+
| | |
JSON-RPC | JSON-RPC | JSON-RPC |
(stdio) | (stdio) | (stdio) |
| | |
+--------v--------+ +----v-----+ +-------v-------+
| Pyright | | tsserver | | gopls |
| (subprocess) | |(subproc) | | (subprocess) |
+-----------------+ +----------+ +---------------+
Communication Protocol¶
LSP uses JSON-RPC 2.0 over stdio:
- Requests: Client sends method + params, server responds
- Notifications: Client sends without expecting response
- Headers:
Content-Length: N\r\n\r\n{json}
Installation Requirements¶
Supported Language Servers¶
| Language | Server | Command |
|---|---|---|
| Python | Pyright | pyright-langserver --stdio |
| TypeScript | typescript-language-server | typescript-language-server --stdio |
| JavaScript | typescript-language-server | typescript-language-server --stdio |
| Go | gopls | gopls serve |
Server Installation¶
Python (Pyright)¶
Note: Pyright requires Node.js 14+.
TypeScript/JavaScript¶
# Install typescript-language-server and typescript
npm install -g typescript typescript-language-server
# Verify installation
typescript-language-server --version
Go (gopls)¶
# Install gopls
go install golang.org/x/tools/gopls@latest
# Verify it's in PATH
# If ~/go/bin is not in PATH, add it:
export PATH=$PATH:~/go/bin
# Verify installation
gopls version
Search Locations¶
mcp-scan searches for servers in these locations:
- System
$PATH ~/go/bin(Go binaries)~/.local/bin/usr/local/bin/opt/homebrew/bin(macOS ARM)~/.npm-global/bin~/node_modules/.bin
CLI Usage¶
Enable LSP¶
# Scan with LSP enabled
mcp-scan scan --lsp ./my-mcp-server
# Scan with LSP debug mode
LSP_DEBUG=1 mcp-scan scan --lsp ./my-mcp-server
YAML Configuration¶
Progress Output¶
[*] Scanning ./my-mcp-server...
[*] LSP: enabled
[*] Discovered 5 files
[*] Running pattern analysis...
[+] Scan completed in 2.3s
[+] Found 3 findings (2 high, 1 medium)
Debug Mode¶
With LSP_DEBUG=1:
[LSP] Found 5 symbols, 4 functions in server.py
[LSP] Analyzing tool get_company_data at server.py:31:4
[LSP] type: (function) def get_company_data(data_type: str) -> str
[LSP] call graph: get_company_data -> 1 callees
LSP Engine Capabilities¶
Supported Operations¶
| Operation | LSP Method | Use |
|---|---|---|
| Hover | textDocument/hover |
Get types and documentation |
| Definition | textDocument/definition |
Go to definition |
| References | textDocument/references |
Find all references |
| Document Symbols | textDocument/documentSymbol |
List file symbols |
| Call Hierarchy | textDocument/prepareCallHierarchy |
Prepare call hierarchy |
| Incoming Calls | callHierarchy/incomingCalls |
Who calls a function |
| Outgoing Calls | callHierarchy/outgoingCalls |
What a function calls |
Detection Flow¶
1. Identify Tool Handler
|
v
2. Open document in LSP server
|
v
3. Get document symbols (GetDocumentSymbols)
|
v
4. Find handler function by name
|
v
5. Get type with hover on SelectionRange
|
v
6. Check for dangerous types (Any, object, unknown)
|
v
7. Build call graph from handler
|
v
8. Search for dangerous sinks in the graph
|
v
9. Generate findings with type context
|
v
10. Close document
LSP Detection Rules¶
LSP-TYPE-001: Dangerous Type Pattern¶
Severity: Medium Confidence: Medium
Detects when a tool handler has a dangerous type like Any, object, or unknown.
# DETECTED: parameter with Any type
from typing import Any
@mcp.tool()
async def process_data(data: Any): # LSP-TYPE-001
# data can be anything
eval(str(data))
Dangerous types:
| Type | Language | Risk |
|---|---|---|
Any |
Python | No type checking |
object |
Python/TS | Generic, no restrictions |
unknown |
TypeScript | Unsafe type |
any |
TypeScript | No type checking |
LSP-TYPE-002: Parameter with Loose Type¶
Severity: Low Confidence: Medium
Detects parameters in the tool schema with loose types.
@mcp.tool(
schema={
"properties": {
"data": {"type": "object"} # LSP-TYPE-002
}
}
)
async def process(data):
pass
LSP-SINK-001: Tool Input Reaches Dangerous Sink¶
Severity: High Confidence: Medium
Detects when a tool handler can reach a dangerous sink through the call graph.
@mcp.tool()
async def execute_command(cmd: str):
# LSP builds call graph and finds path to os.system
helper(cmd)
def helper(command):
run_shell(command)
def run_shell(cmd):
os.system(cmd) # LSP-SINK-001: Dangerous sink reached
Tracked dangerous sinks:
| Sink | Type | Language |
|---|---|---|
os.system |
Command execution | Python |
subprocess.run |
Command execution | Python |
subprocess.Popen |
Command execution | Python |
eval |
Code evaluation | Python/JS |
exec |
Code execution | Python |
open |
File access | Python |
sqlite3.execute |
SQL execution | Python |
cursor.execute |
SQL execution | Python |
requests.get |
HTTP request | Python |
urllib.request |
HTTP request | Python |
child_process.exec |
Command execution | Node.js |
child_process.spawn |
Command execution | Node.js |
Call Graph Construction¶
Algorithm¶
1. PrepareCallHierarchy at handler position
|
v
2. Create root node with symbol information
|
v
3. For each depth up to maxDepth:
|
+-- GetOutgoingCalls from current node
| |
| v
+-- For each call:
| |
| +-- Create node for the callee
| |
| +-- Add edge with call sites
| |
| +-- Recurse if not visited
|
+-- Mark node as visited
Analysis Depth¶
| Depth | Use Case |
|---|---|
| 1-2 | Quick checks, direct calls only |
| 3-5 | Standard analysis, covers most vulnerabilities |
| 5-10 | Deep analysis, complex call chains |
| 10+ | Exhaustive analysis, performance impact |
Default: mcp-scan uses depth 5.
Performance¶
Startup Times¶
| Server | Cold start | With cache |
|---|---|---|
| Pyright | 2-5s | <100ms |
| tsserver | 1-3s | <100ms |
| gopls | 1-2s | <100ms |
Memory Usage¶
| Server | Base memory | Per 1000 files |
|---|---|---|
| Pyright | ~150MB | +50MB |
| tsserver | ~200MB | +80MB |
| gopls | ~100MB | +30MB |
Optimizations¶
- Client cache: The manager keeps active clients between analyses
- Document opened once: Opened at start and closed at end
- Visitation tracking: Call graph doesn't revisit already visited nodes
Integration with Other Engines¶
With Pattern Engine¶
LSP complements pattern rules by providing type context:
Pattern Engine LSP Detector
| |
| Detects call | Verifies type
| to subprocess.run | of argument
| |
+----------+-----------+
|
v
Combined finding:
- Pattern: MCP-A001
- LSP: str type verified
- Confidence: HIGH
With Taint Engine¶
LSP improves taint analysis precision:
Taint Engine LSP
| |
| Detects flow | Gets types
| source -> sink | at each step
| |
| May lose type | Confirms that
| in propagation | type is maintained
| |
+-------------+------------+
|
v
Trace with types:
[str] -> [str] -> [str] -> sink
Limitations¶
Current Limitations¶
| Limitation | Impact | Mitigation |
|---|---|---|
| Requires installed server | Won't work without server | Clear error message |
| Slow startup | First execution slow | Client cache |
| Large projects | High memory usage | Limit analyzed files |
| Dynamic code | Can't follow getattr |
Fallback to pattern matching |
| Cross-file limited | Depends on server | gopls has better support |
Unsupported Cases¶
# LSP cannot follow this:
func = getattr(module, "dangerous_" + user_input)
func()
# LSP cannot analyze eval:
code = f"subprocess.run('{cmd}')"
eval(code)
# Metaprogramming:
class Meta(type):
def __new__(cls, name, bases, dct):
dct['run'] = lambda self, x: os.system(x)
return super().__new__(cls, name, bases, dct)
Troubleshooting¶
Common Problems¶
Problem: Server won't start
Solution: Install the server globally:Problem: gopls not found
Solution: Ensure ~/go/bin is in PATH:Problem: No type information obtained
Solution: Verify the project has valid configuration (pyproject.toml, tsconfig.json, go.mod).Problem: Slow analysis
Solution:
1. Reduce call graph depth
2. Limit analyzed files
3. Use --mode fast
Verify Installation¶
# Python
pyright-langserver --version
# TypeScript
typescript-language-server --version
# Go
gopls version
# Run with debug to see which servers are found
LSP_DEBUG=1 mcp-scan scan --lsp ./project
References¶
- LSP Specification
- Pyright
- typescript-language-server
- gopls
- Pattern Engine
- Taint Analysis
- Vulnerability Classes
This documentation is for security analysts. For general usage, see user-guide/.