sanitized-db-mcp

sanitized-db-mcp

Enables SQL query sanitization by rewriting queries at the AST level to prevent PII/PHI exposure, acting as a proxy between AI agents and PostgreSQL databases.

Category
访问服务器

README

Sanitized DB MCP Server

PyPI Docker Tests

An MCP server that rewrites SQL queries at the AST level to prevent PII/PHI exposure.

Why This Exists

AI agents write SQL. They also hallucinate column names, ignore access controls, and cheerfully SELECT * from tables full of personal data. This server sits between the agent and your PostgreSQL database, rewriting every query so hidden columns return type-preserving placeholders instead of real values. The agent gets useful results; your users keep their privacy.

Quick Start

1. Create an allowlist

Generate a scaffold from your database schema:

# Install the CLI (skip if using uvx or Docker below)
pip install sanitized-db-mcp

sanitized-db-mcp generate-allowlist --database-url postgresql://user:pass@host:5432/mydb > allowlist.yaml

Edit the YAML to expose only the columns agents should see (see Generating an Allowlist below).

2. Run the server

Pick one of the four methods below and add the config to your .mcp.json.

Method 1: uvx (recommended — zero install)

uvx runs the package in an isolated environment with no permanent installation and no dependency conflicts. Unlike pip, there is nothing to install or manage. Unlike Docker, there are no volume mounts or path mappings to configure.

{
  "sanitized-db": {
    "type": "stdio",
    "command": "uvx",
    "args": ["sanitized-db-mcp"],
    "env": {
      "ALLOWLIST_PATH": "./allowlist.yaml",
      "DATABASE_URL": "postgresql://user:pass@host:5432/mydb"
    }
  }
}

No install needed. uvx downloads and runs the package in an isolated environment.

Method 2: pip install

pip install sanitized-db-mcp
{
  "sanitized-db": {
    "type": "stdio",
    "command": "python3",
    "args": ["-m", "sanitized_db_mcp.server"],
    "env": {
      "ALLOWLIST_PATH": "./allowlist.yaml",
      "DATABASE_URL": "postgresql://user:pass@host:5432/mydb"
    }
  }
}

Method 3: Docker (pre-built image)

{
  "sanitized-db": {
    "type": "stdio",
    "command": "docker",
    "args": [
      "run", "-i", "--rm",
      "-e", "ALLOWLIST_PATH=/app/allowlist.yaml",
      "-e", "DATABASE_URL",
      "-v", "./allowlist.yaml:/app/allowlist.yaml:ro",
      "ghcr.io/ruminaider/sanitized-db-mcp:latest"
    ],
    "env": {
      "DATABASE_URL": "postgresql://user:pass@host:5432/mydb"
    }
  }
}

Method 4: Docker (build from source)

docker build -t sanitized-db-mcp:local .
{
  "sanitized-db": {
    "type": "stdio",
    "command": "docker",
    "args": [
      "run", "-i", "--rm",
      "-e", "ALLOWLIST_PATH=/app/allowlist.yaml",
      "-e", "DATABASE_URL",
      "-v", "./allowlist.yaml:/app/allowlist.yaml:ro",
      "sanitized-db-mcp:local"
    ],
    "env": {
      "DATABASE_URL": "postgresql://user:pass@host:5432/mydb"
    }
  }
}

3. Query through the agent

The server exposes a single MCP tool (query) that accepts raw SQL and returns sanitized results.

Generating an Allowlist

The CLI tool connects to your database, reads the schema, and produces a YAML file where nothing is visible by default. You opt columns in by uncommenting them.

sanitized-db-mcp generate-allowlist --database-url postgresql://user:pass@host:5432/mydb > allowlist.yaml

Add --deny-pii to flag columns that look like PII (email, name, phone, address, etc.):

sanitized-db-mcp generate-allowlist --database-url postgresql://... --deny-pii > allowlist.yaml

Example output:

# Generated by: sanitized-db-mcp generate-allowlist --deny-pii
#
# HOW TO USE:
# - Columns under "columns:" are VISIBLE to agents (currently empty)
# - Commented lines show available columns — uncomment to make visible
# - Lines marked "# PII" were flagged as likely PII/PHI — review carefully
# - After editing, restart the MCP server to apply changes

tables:
  users:
    columns: {}
    # Available columns (uncomment to make visible):
    #   id: {type: integer, placeholder: 0}
    #   is_active: {type: boolean, placeholder: false}
    #   email: {type: varchar, placeholder: '[REDACTED]'}  # PII
    #   password: {type: varchar, placeholder: '[REDACTED]'}  # PII

Uncomment the columns you want agents to see. Leave everything else hidden.

Allowlist YAML Format

tables:
  <table_name>:
    columns:
      <column_name>: {type: <pg_type>, placeholder: <sql_literal>}
allowed_functions:
  - FUNCTION_NAME
  • Listed columns are VISIBLE. Unlisted columns are hidden and replaced with type-preserving placeholders.
  • type: base PostgreSQL type (integer, varchar, boolean, timestamp, uuid, jsonb, etc.).
  • placeholder: SQL literal that replaces hidden column values. Must be type-compatible.
  • allowed_functions: SQL functions agents can call. All others are rejected.

Configuration

Variable Required Default Description
ALLOWLIST_PATH Yes -- Path to allowlist.yaml
MCP_SERVER_NAME No sanitized-db MCP server name (affects tool name: mcp__<name>__query)
DATABASE_URL If not using Render -- PostgreSQL connection string
RENDER_POSTGRES_ID If using Render -- Render Postgres instance ID
RENDER_API_KEY If using Render -- Render API bearer token

The server prefers Render API credentials when both are set. For local development, DATABASE_URL is sufficient.

Framework Integration

  • Django: Use a visible() field decorator to mark safe fields, then generate the allowlist from model metadata.
  • Rails / Other: Use the CLI tool to scaffold, then curate manually. Or build your own generator that outputs the same YAML format.
  • Custom generators: The YAML format is the contract. Any tool that produces conformant YAML works.

SSE Transport (Remote Deployment)

For shared deployments (e.g., Render.com), the server supports SSE transport over HTTP.

Installation

pip install 'sanitized-db-mcp[sse]'

Configuration

Variable Required Default Description
MCP_TRANSPORT No stdio Transport mode: stdio or sse
PORT No 8000 HTTP port (Render sets this automatically)
MCP_API_KEY Recommended Bearer token for HTTP authentication
MCP_MAX_CONNECTIONS No unlimited Max concurrent connections (uvicorn limit_concurrency)
MCP_SESSION_TIMEOUT No unlimited Max SSE session duration in seconds

Running

export MCP_TRANSPORT=sse
export MCP_API_KEY=your-secret-key
export ALLOWLIST_PATH=./allowlist.yaml
export DATABASE_URL=postgresql://...
python -m sanitized_db_mcp.server

Docker

docker run -e MCP_TRANSPORT=sse -e MCP_API_KEY=secret -e ALLOWLIST_PATH=/app/allowlist.yaml \
  -e DATABASE_URL=postgresql://... -p 8000:8000 sanitized-db-mcp

Claude Code Client Configuration

In your MCP client config, point to the SSE endpoint:

{
  "mcpServers": {
    "sanitized-db": {
      "type": "sse",
      "url": "https://your-service.onrender.com/sse",
      "headers": {
        "Authorization": "Bearer your-secret-key"
      }
    }
  }
}

Endpoints

  • GET /sse — SSE connection (MCP session)
  • POST /messages/ — Client-to-server messages
  • GET /health — Health check (no auth required)

Deployment Guidance

Connection limits: Set MCP_MAX_CONNECTIONS to prevent resource exhaustion under attack or misconfiguration. A reasonable value is 2-5x your expected concurrent clients (e.g., 100 when expecting 10-20 clients). New connections beyond the limit receive HTTP 503. Note: each SSE session AND each /messages/ POST from that session count as separate concurrent connections, so do not set this too low.

Session timeout: Set MCP_SESSION_TIMEOUT (seconds) to close abandoned SSE sessions. 28800 (8 hours) is a reasonable starting point. Clients reconnect automatically after timeout. Without this, abandoned sessions consume resources indefinitely since uvicorn's timeout_keep_alive does not apply to active SSE streams.

CORS: CORS headers are intentionally omitted. The server's clients (Claude Code, MCP CLI tools) are non-browser applications that ignore CORS. The native browser EventSource API cannot send Authorization headers, so browsers cannot authenticate even if they attempt cross-origin connections. If browser-based MCP clients become a supported consumer, add Starlette's CORSMiddleware.

Audit logging: Every query is logged as structured JSON with client IP, request ID, session ID, and user agent for HIPAA compliance. Behind a reverse proxy (Render, nginx), client IP is extracted from X-Forwarded-For. Configure your log aggregator for 6-year retention per HIPAA requirements.

How the Sanitizer Works

The server exposes a single MCP tool (query) that accepts raw SQL and returns sanitized results. Every query passes through an 11-step pipeline:

Agent sends SQL
      |
      v
1.  Parse (pglast) ───── syntax error? → QuerySyntaxError
      |
      v
2.  Statement type ───── not SELECT? → StatementTypeError
      |                   SELECT INTO? → StatementTypeError
      |                   FOR UPDATE/SHARE? → StatementTypeError
      v
3.  Table validation ─── system catalog? → SystemCatalogError
      |                   not in allowlist? → RestrictedColumnError
      |                   TABLESAMPLE? → unwrap, validate inner table
      v
4.  Function check ───── always-blocked? → DisallowedFunctionError
      |                   not in allowlist? → DisallowedFunctionError
      |                   FILTER (WHERE ...)? → walk with WHERE rules
      |                   inline OVER clause? → walk with WHERE rules
      v
5.  WHERE/JOIN check ─── hidden column? → RestrictedColumnError
      v
6.  Clause check ──────── ORDER BY / GROUP BY / DISTINCT ON / WINDOW
      |                    hidden column? → RestrictedColumnError
      v
7.  Subquery check ───── hidden column in subquery/CTE SELECT? → RestrictedColumnError
      v
8.  Rewrite SELECT ───── hidden columns → type-preserving placeholders
      |                   SELECT * → visible columns + redaction marker
      v
9.  Serialize AST ────── rewritten SQL string
      v
10. Execute ───────────── read-only, 5s timeout, SSL
      v
11. Audit log ─────────── structured JSON (original, rewritten, outcome)

Security Model

All 200 tests pass with 0 xfails. The pen test suite covers 21 attack categories:

Category Tests Defense
ORDER BY / GROUP BY / DISTINCT ON 13 sortClause, groupClause, distinctClause walked with WHERE rules
Window function attacks 6 Named WINDOW and inline OVER walked with WHERE rules
Aggregate FILTER clause 4 agg_filter walked with WHERE rules
CTE attacks 6 CTE SELECT targets validated; unused CTEs with hidden columns rejected
LATERAL join attacks 3 Correlated subquery WHERE clauses validated
Schema/identifier tricks 8 Quoted identifiers, unicode escapes, pg_temp schema handled
Composite type / row attacks 3 Field selection rejected; ROW() with hidden columns redacted
ARRAY attacks 3 ARRAY subquery, ARRAY_AGG, ARRAY[] with hidden columns caught
JSONB operator attacks 5 ->, ->>, #>, @>, ? operators on hidden columns caught
Type cast attacks 4 Chained casts on hidden columns redacted; casts in WHERE rejected
FROM clause variants 3 TABLESAMPLE unwrapped, VALUES and generate_series handled
Locking clauses 3 FOR UPDATE, FOR SHARE rejected at sanitizer level
Subquery nesting 5 Triple-nested, correlated, NOT EXISTS, ANY all validated
Statement type / multi-statement 5 SELECT INTO rejected; null byte, comment, dollar quoting tested
Ambiguous column resolution 3 Unqualified columns conservatively resolved
Encoding edge cases 4 Cyrillic homoglyphs, unicode escapes, semicolons in aliases
Timing / side-channel 4 pg_sleep, amplification, cross-join, recursive CTE blocked
Error-based extraction 4 Generic error messages; no column/table names leaked
Connection security 5 SSL, timeout, read-only, autocommit, no connection strings in errors
Allowlist integrity 4 Case-insensitive lookup, unknown columns default to hidden
Audit logging 4 All outcomes covered, HIPAA fields present, finally-block guarantee

Running Tests

cd sanitized-db-mcp

# Full test suite (200 tests across 3 suites)
python -m pytest sanitized_db_mcp/tests/ -v

# Individual suites
python -m pytest sanitized_db_mcp/tests/test_sanitizer.py -v    # Core rewriting (37 tests)
python -m pytest sanitized_db_mcp/tests/test_bypass.py -v       # Bypass resistance (64 tests)
python -m pytest sanitized_db_mcp/tests/test_pentest.py -v      # Pen test (99 tests)
python -m pytest sanitized_db_mcp/tests/test_allowlist.py -v    # Allowlist loader

Key Files

File Purpose
server.py MCP server entry point, query(sql) tool
sanitizer.py AST-level SQL rewriting engine
allowlist.py In-memory allowlist representation
connection.py Render API + static connection management
errors.py Sanitized error classes (no schema leakage)
audit.py HIPAA-compliant query audit logging

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选