Using Pipelock with Claude Code#
Pipelock sits between Claude Code and MCP servers, scanning every response for
prompt injection before it reaches the agent. This guide covers both MCP proxy
mode and HTTP fetch proxy mode.
Quick Start#
# 1. Install pipelock
go install github.com/luckyPipewrench/pipelock/cmd/pipelock@latest
# 2. Verify it works
pipelock version
# 3. Wrap an MCP server
pipelock mcp proxy --config configs/claude-code.yaml -- npx -y @modelcontextprotocol/server-filesystem /tmp
MCP Proxy Mode#
Pipelock wraps any MCP server as a stdio proxy with bidirectional scanning.
Client requests are scanned for DLP leaks and injection in tool arguments.
Server responses are scanned for prompt injection before forwarding to the client.
Tool descriptions are scanned for poisoned instructions and tracked for rug-pull changes.
# Local subprocess (stdio)
Claude Code <--> pipelock mcp proxy <--> MCP Server
(client) (scan both directions) (subprocess)
# Remote server (Streamable HTTP)
Claude Code <--> pipelock mcp proxy --upstream <--> Remote MCP Server
(client) (scan both directions) (HTTP)
How It Works#
- Pipelock starts the MCP server as a child process (or connects to
--upstreamURL) - Client requests (stdin) are scanned for DLP patterns, env leaks, and injection
- Clean requests are forwarded; flagged requests are blocked or warned per config
- Server responses (stdout) are scanned line-by-line for injection patterns
- Clean responses are forwarded; threats trigger the configured action
- Server stderr is forwarded to pipelock's stderr for diagnostics
Actions#
| Action | Behavior | Use When |
|---|---|---|
warn | Log detection, forward response unchanged | Tuning patterns, low-risk environments |
block | Replace response with JSON-RPC error (-32000) | Production, unattended agents |
strip | Redact matched patterns, forward modified response | When partial content is acceptable |
ask | Terminal y/N/s prompt with timeout (requires TTY) | Attended sessions, manual review |
Configuring Claude Code#
Project-Level (.mcp.json)#
Create .mcp.json in your project root. This is shared via git so all team
members get the same MCP security configuration.
{
"mcpServers": {
"filesystem": {
"command": "pipelock",
"args": [
"mcp", "proxy",
"--config", "pipelock.yaml",
"--",
"npx", "-y", "@modelcontextprotocol/server-filesystem", "/tmp"
]
}
}
}
User-Level (~/.claude.json)#
For personal MCP servers that shouldn't be in git:
{
"mcpServers": {
"filesystem": {
"command": "pipelock",
"args": [
"mcp", "proxy",
"--config", "/home/you/.config/pipelock.yaml",
"--",
"npx", "-y", "@modelcontextprotocol/server-filesystem", "/home/you/projects"
]
}
}
}
Multiple Servers#
Wrap each MCP server independently:
{
"mcpServers": {
"filesystem": {
"command": "pipelock",
"args": ["mcp", "proxy", "--config", "pipelock.yaml", "--",
"npx", "-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
},
"postgres": {
"command": "pipelock",
"args": ["mcp", "proxy", "--config", "pipelock.yaml", "--",
"npx", "-y", "@modelcontextprotocol/server-postgres",
"postgresql://localhost/mydb"]
},
"github": {
"command": "pipelock",
"args": ["mcp", "proxy", "--config", "pipelock.yaml", "--",
"npx", "-y", "@modelcontextprotocol/server-github"]
}
}
}
Remote MCP Servers (Streamable HTTP)#
For MCP servers accessible over HTTP (e.g., hosted services, Docker containers),
use --upstream instead of --:
{
"mcpServers": {
"remote-tools": {
"command": "pipelock",
"args": [
"mcp", "proxy",
"--config", "pipelock.yaml",
"--upstream", "http://localhost:8080/mcp"
]
}
}
}
Pipelock bridges Claude Code's stdio transport to the upstream HTTP server,
applying the same bidirectional scanning as subprocess mode. Session IDs are
tracked automatically, and a GET SSE stream is opened for server-initiated
messages when the server supports it.
HTTP Fetch Proxy Mode#
For scanning URLs fetched by Claude Code (via WebFetch or other tools), run
pipelock as an HTTP proxy server:
# Start the proxy (background or separate terminal)
pipelock run --config configs/claude-code.yaml
The proxy listens on 127.0.0.1:8888 by default and exposes:
| Endpoint | Purpose |
|---|---|
/fetch?url=<target> | Fetch a URL through the scanner |
/health | Health check |
/metrics | Prometheus metrics |
/stats | JSON statistics |
Claude Code Hooks (PreToolUse)#
Pipelock integrates with Claude Code's hook system to scan tool calls before
execution. The pipelock claude setup command installs hooks automatically:
# Install hooks (writes to ~/.claude/settings.json)
pipelock claude setup
# Or install to a project-local settings file
pipelock claude setup --project
# Preview without writing
pipelock claude setup --dry-run
# Remove hooks
pipelock claude remove
This registers pipelock as a PreToolUse hook for security-relevant tools:
| Matcher | Tools | What's scanned |
|---|---|---|
Bash|WebFetch|Write|Edit | Built-in tools | Commands, URLs, file content for DLP and policy |
mcp__.* | All MCP tools | Tool arguments for DLP and injection |
Unknown tools (Read, Glob, Grep, Agent, etc.) are allowed by default.
The hook reads JSON from stdin and returns allow/deny decisions. An --exit-code
mode is also available (exit 0 for allow, exit 2 for deny):
# Default mode (JSON response on stdout)
echo '{"session_id":"s1","hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"ls"},"tool_use_id":"t1"}' | pipelock claude hook
# Exit-code mode
echo '...' | pipelock claude hook --exit-code
TLS Interception#
When using pipelock as an HTTP forward proxy, CONNECT tunnels are opaque by
default: pipelock only sees the hostname, not the request body or response
content. Enabling TLS interception closes this gap by performing a MITM on
HTTPS connections, giving you full DLP and response injection detection through
CONNECT tunnels.
To enable it:
- Generate a CA and enable TLS interception (see the TLS Interception Guide)
- Trust the CA for Node.js (used by Claude Code's MCP servers):
export NODE_EXTRA_CA_CERTS=~/.pipelock/ca.pem
MCP proxy mode (stdio wrapping) and Claude Code hooks do not require TLS
interception. They scan traffic directly without certificates.
Choosing a Config#
Pipelock ships with agent-specific presets in configs/:
| Preset | Action | Entropy | Rate Limit | Best For |
|---|---|---|---|---|
claude-code.yaml | block | 5.0 | 120/min | Claude Code (unattended) |
cursor.yaml | block | 5.0 | 120/min | Cursor IDE (unattended) |
generic-agent.yaml | warn | 5.5 | 120/min | New agents (tuning phase) |
balanced.yaml | warn | 4.5 | 60/min | General purpose |
strict.yaml | block | 3.5 | 30/min | High-security environments |
Start with generic-agent.yaml if you're unsure. Once you've verified there
are no false positives for your workflow, switch to claude-code.yaml or
strict.yaml.
Troubleshooting#
MCP server not starting#
Verify the command works without pipelock first:
# Test the server directly
npx -y @modelcontextprotocol/server-filesystem /tmp
# Then wrap it
pipelock mcp proxy -- npx -y @modelcontextprotocol/server-filesystem /tmp
Config file not found#
Use an absolute path in .mcp.json if the relative path doesn't resolve:
"args": ["mcp", "proxy", "--config", "/absolute/path/to/pipelock.yaml", "--", ...]
Input scanning and injection patterns#
MCP input scanning reuses your response_scanning.patterns for injection
detection in tool arguments. If you configure custom injection patterns, they
apply to both directions. DLP patterns work independently.
False positives#
If legitimate responses are being blocked, switch to warn mode first to see
what's being flagged:
response_scanning:
action: warn # log but don't block
Check stderr output for detection logs, then adjust patterns or thresholds.
Seeing pipelock output#
Pipelock logs to stderr (which Claude Code captures). To see real-time output
during development, run the MCP server manually:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | \
pipelock mcp proxy --config configs/claude-code.yaml -- npx -y @modelcontextprotocol/server-filesystem /tmp