noblepayne/mcp-injector
HTTP shim for injecting MCP tools into OpenAI-compatible chat completions
mcp-injector
Resilient LLM gateway shim with virtual models, provider fallbacks, and secure MCP tool injection
mcp-injector sits between an agent (like OpenClaw) and LLM gateways. It provides automatic failover, error translation, and a secure governance framework for MCP tool execution.
Key Features
- ✅ Virtual model chains - Define fallback providers with cooldowns.
- ✅ Transparent Loop Preservation - Accumulates all internal turns (reasoning, tool calls, results) and synchronizes them with clients via signed footers.
- ✅ Session Re-hydration - Automatically restores conversation context from durable storage when a
session-idis provided. - ✅ Governance Framework - Declarative tool access policies (Permissive/Strict).
- ✅ PII Scanning & Restoration - Automatic redaction of sensitive data in prompts. Trusted tools can receive original PII values for secure processing.
- ✅ Signed Audit Trail - Tamper-proof NDJSON logs with ULID and HMAC chaining.
- ✅ Provider-Level Observability - Granular tracking of tokens, requests, and rate-limits per provider.
- ✅ Multi-transport MCP - Support for HTTP and STDIO (local process) MCP servers.
- ✅ Error translation - Converts cryptic provider errors into actionable messages.
Governance & Security
mcp-injector includes a robust governance layer configured via the :governance key in mcp-servers.edn (copy from mcp-servers.example.edn).
Governance Modes
:permissive(Default): All tools are allowed unless explicitly denied.:strict: All tools are denied unless explicitly allowed in the policy.
Privileged Tools
Certain high-risk tools (like clojure-eval) are marked as Privileged. These tools are always blocked by default, even in permissive mode, unless explicitly listed in an :allow rule.
Example Policy
:governance
{:mode :permissive
:policy
{:mode :permissive ; Fallback mode for this policy (overrides global)
:allow ["mcp__stripe__*"]
:deny ["mcp__danger-server__*"]
:rules [{:model "gpt-4o-mini" :deny ["clojure-eval"]}]
:sampling {:trusted-servers ["stripe" "postgres"]}}
:audit
{:enabled true :path "logs/audit.log.ndjson"}
:pii
{:enabled true :mode :replace :proximity-check-enabled true}}PII Hardening & False Positive Reduction
The PII scanner uses a multi-layered approach to minimize false positives while catching real secrets:
- Whitelisting: Local file paths (Windows/POSIX), URLs, IP addresses, and UUIDs are automatically ignored.
- Character Diversity: Tokens must contain at least 4 character classes (lower, upper, digit, special) OR 3 classes with at least 20 characters. This prevents descriptive strings like
mcp__stripe__retrieve_customerfrom being flagged. - Proximity Check: By default, general high-entropy strings are only redacted if they follow assignment-like keywords (e.g.,
api_key:,token =). Explicit regex patterns (AWS, Anthropic, etc.) bypass this check for maximum safety.
PII Restoration (Smart Vault)
For tools that need access to original PII data (e.g., a Stripe integration that must see real email addresses), configure trust levels:
:servers
{:stripe
{:url "http://localhost:3001/mcp"
:trust :restore ; :none (default), :read, or :restore
:tools [{:name "retrieve_customer" :trust :restore}]}}:none(default): Tool receives redacted tokens like[EMAIL_ADDRESS_a35e2662]:restore: Tool receives original values (e.g.,wes@example.com)
The vault uses deterministic SHA-256 hashing with a per-request salt, ensuring tokens are consistent within a request but not leakable across requests.
⚠️ Security Notice: clojure-eval Escape Hatch
The clojure-eval tool is a privileged escape hatch that allows the LLM to execute arbitrary Clojure code on the host JVM. This is Remote Code Execution (RCE) by design.
- Default State: Disabled. You must explicitly allow
clojure-evalin your policy's:allowlist. - Risk: If enabled, a compromised, hallucinating, or prompt-injected LLM gains full system access—including files, environment variables, network, and process control.
- Mitigation: Only enable
clojure-evalfor highly trusted models in isolated environments. Treat it as root-level access. - Startup Warning: When enabled, mcp-injector logs a
CRITICALaudit event at startup.
Observability & Tracing
Action Receipts — When tools run during a request, mcp-injector prepends a clean markdown receipt to the response content:
Action Receipt: trace_id=abc123 | 2 tools (165ms total)
- stripe.get_customer: 45ms
- postgres.query: ERROR: timeout
---
Receipts are:
- Prepended (not appended) — better for Telegram/Slack UX
- PII-masked — sensitive values replaced with tokens
- Controllable via config/env var
Response Headers — Every response includes W3C trace headers for distributed tracing:
X-Injector-Traceparent: 00-<trace-id>-<parent-id>-00
Configure observability:
{:receipt-mode :on} ;; :on, :off, or :errors-only
{:receipt-style :emoji} ;; :emoji (future) or :ascii
{:footer-mode :off} ;; :off (default) or :legacyOr via environment variables:
MCP_INJECTOR_RECEIPT_MODE=on|off|errors-only
MCP_INJECTOR_RECEIPT_STYLE=emoji|ascii
MCP_INJECTOR_FOOTER_MODE=off|legacyPer-request override: extra_body: {:receipt false} suppresses the receipt for that request.
Quick Start
Prerequisites
Installation
nix develop
bb test
bb runConfiguration
Copy the example config and customize:
cp mcp-servers.example.edn mcp-servers.ednEnvironment Variables
| Variable | Description | Default |
|---|---|---|
INJECTOR_AUDIT_SECRET |
32-byte secret for signing audit log entries. | (auto-generated) |
MCP_INJECTOR_AUDIT_LOG_PATH |
Path for NDJSON audit log. | logs/audit.log.ndjson |
MCP_INJECTOR_PORT |
Server port. | 8080 |
MCP_INJECTOR_LLM_URL |
LLM gateway endpoint. | http://localhost:11434 |
MCP_INJECTOR_MAX_ITERATIONS |
Agent loop iteration limit. | 10 |
MCP_INJECTOR_LOG_LEVEL |
Log verbosity. | debug |
MCP_INJECTOR_TIMEOUT_MS |
LLM request timeout. | 1800000 |
MCP_INJECTOR_RECEIPT_MODE |
When to show action receipts: on, off, or errors-only. |
on |
MCP_INJECTOR_RECEIPT_STYLE |
Receipt style: emoji or ascii. |
emoji |
MCP_INJECTOR_FOOTER_MODE |
Legacy HTML footer: off or legacy. |
off |
Edit mcp-servers.edn:
{:servers
{:stripe
{:url "http://localhost:3001/mcp"
:tools ["retrieve_customer" "list_charges"]}}
:llm-gateway
{:url "http://localhost:8080"
:virtual-models
{:brain
{:chain ["provider1/model1" "provider2/model2"]
:cooldown-minutes 5}}}}Control API
GET /api/v1/status: Health and version.GET /api/v1/mcp/tools: List discovered tools.GET /api/v1/stats: Usage statistics broken down by model and provider.GET /api/v1/audit/verify: Cryptographically verify the audit log integrity.POST /api/v1/mcp/reset: Clear caches and restart processes.
NixOS Deployment
services.mcp-injector = {
enable = true;
mcpServers = { ... };
governance = {
mode = "permissive";
policy = {
allow = [ "mcp__stripe__*" ];
};
};
};Status: Production-ready | Tests: 72 passing | Built with: Babashka + http-kit + Cheshire