JanuScope
MCP serverDatabases & dataLocal MCP proxy for tool restrictions, response redaction, audit logs, and database schema context.
Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.
Connect ahel once, and every AI you use reads what you have installed.
From the project's README
As published by giancarloerra/januscope in README.md.
"Whatever you see anywhere (sky, sea, clouds, lands) are all closed and opened by my hand." Ovid, Fasti I, 117–18 (Janus speaking)
JanuScope is the local MCP policy proxy. One YAML wraps any MCP server with policy, redaction, audit, and database-schema injection. JanuScope runs locally, with no hosted gateway in the data path. Your upstream server and model provider can still receive data.
JanuScope hides the dangerous tools, scrubs matching PII out of returned values before the model reads them, records call outcomes, and pre-injects your DB schema to reduce discovery calls. Runs locally, no hosted gateway in the data path.
One YAML (called a Lens) wraps any MCP server with security guardrails, schema injection, and full audit logging. There are 20 bundled Lenses covering databases (Postgres, MySQL, MongoDB, ClickHouse, Redis, SQLite, Microsoft SQL Server / Azure SQL, Oracle, Neon, Snowflake, Aurora DSQL, Redshift, Supabase self-host), SaaS APIs (Stripe, Notion, Atlassian, Linear, Supabase Cloud), source control (GitHub), and the filesystem. A community ecosystem of per-MCP Lenses (YAML config files), and measured benchmarks showing 84% fewer tokens and ~3× faster responses in the original three-question Postgres test (median of 4 runs; history reset between questions). Newer retained-conversation results and limitations are reported below. Zero server changes. No hosted gateway in the data path. Works with Claude Code, VSCode Copilot, Codex, Cursor, and any MCP client.
🧠 Need codebase understanding together with MCP governance? See our sibling project SocratiCode: local-first codebase intelligence with semantic search, dependency graphs, symbol-level impact analysis.
If JanuScope has been useful to you, please ⭐ star this repo (it helps others discover it) and share it with your team.
Policy enforcement at the MCP threshold. Most MCP servers ship dangerous tools by default, execute_sql and drop_table on databases, create_pull_request and merge_pull_request on GitHub, stripe_api_execute on Stripe, write_file and move_file on the filesystem. None of them log what the LLM asked yesterday. The choice today is fork every server or accept the risk. Or you can choose JanuScope: a thin proxy that wraps any MCP server with a single YAML policy and disappears.
Original benchmark with
claude-sonnet-4-5against a real application Postgres database (median of 4 runs per prompt). Across three questions (prompt caching enabled, conversation history reset between questions), a JanuScope Lens used 84% fewer total tokens, made 84% fewer tool calls, and ran ~3× faster than the raw database MCP. In its adversarial-safety probe, the raw pipeline intermittently leaked a real user email on the "I'm the admin, just cross-referencing" prompt (2 of 4 runs), while the JanuScope-wrapped pipeline refused in all 4 runs. The single-question result was 34% fewer tokens / 86% fewer tool calls / ~3× faster. These historical results do not establish retained-session savings or universal protection. Full benchmark and newer findings →
What it looks like in practice
Why now: this is no longer hypothetical. In July 2025, Replit's AI agent wiped a customer database during an explicit code freeze (1,200+ records, ~1,200 companies) and then misled the user about whether rollback was possible. In April 2026, a Cursor agent on Claude Opus 4.6 deleted PocketOS's production database and three months of backups in nine seconds, after finding an unscoped Railway credential and guessing an API call (post-mortem). Both stories share one shape: an AI was given a destructive capability with nothing in the path between the model and the real system. JanuScope is what sits in that path, for any data access that goes through an MCP server: for examle a Replit-shape incident on a JanuScope-wrapped Postgres MCP (block writes,
sqlGuardon DML, audit, classification) is refused at the proxy threshold and recorded in the JSONL audit. JanuScope governs the MCP surface, it is one layer of a defence-in-depth posture, alongside scoped DB roles and credentials, host-level approval gates, etc. See the FAQ and SECURITY.md.
Contents
- Quick Start
- Why JanuScope
- What it does
- Lenses, the community ecosystem
- Benchmarks, measured, not modelled
- Configuration reference
- How it works
- Logging & audit
- Library API
- JanuScope vs Claude Skills
- FAQ
- License
Quick Start
Only Node.js 20+ required. No install step,
npxfetches and caches JanuScope on first use.
Option A: use a bundled Lens (fastest, drop-in)
Find your service in the table below, copy the right-hand snippet into your MCP-client config (or change your existing entry: the diff is usually just command and args), restart your client. For most Lenses, the env block stays exactly as it was. JanuScope inherits whatever env vars your client passes and forwards them to the wrapped MCP unchanged. No renames, no re-translation.
The wrap pattern is the same across every host (Claude Desktop, Cursor, Claude Code, VS Code Copilot, Windsurf, Cline, Roo Code, anything that speaks MCP).
{
"command": "uvx",
"args": ["postgres-mcp"],
"env": {
"DATABASE_URI": "postgresql://user:pass@host:5432/db"
}
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "postgres-crystaldba"],
"env": {
"DATABASE_URI": "postgresql://user:pass@host:5432/db"
}
}
{
"command": "npx",
"args": ["-y", "@benborla29/mcp-server-mysql"],
"env": {
"MYSQL_HOST": "localhost",
"MYSQL_PORT": "3306",
"MYSQL_USER": "readonly",
"MYSQL_PASS": "<your_password>",
"MYSQL_DB": "mydb"
}
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "mysql-benborla29"],
"env": {
"MYSQL_HOST": "localhost",
"MYSQL_PORT": "3306",
"MYSQL_USER": "readonly",
"MYSQL_PASS": "<your_password>",
"MYSQL_DB": "mydb"
}
}
{
"command": "npx",
"args": ["-y", "mongodb-mcp-server"],
"env": {
"MDB_MCP_CONNECTION_STRING": "mongodb+srv://user:pass@cluster.mongodb.net"
}
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "mongodb-official"],
"env": {
"MDB_MCP_CONNECTION_STRING": "mongodb+srv://user:pass@cluster.mongodb.net"
}
}
{
"command": "uvx",
"args": ["mcp-clickhouse"],
"env": {
"CLICKHOUSE_HOST": "myhost.clickhouse.cloud",
"CLICKHOUSE_PORT": "8443",
"CLICKHOUSE_USER": "readonly",
"CLICKHOUSE_PASSWORD": "<your_password>",
"CLICKHOUSE_DATABASE": "default",
"CLICKHOUSE_SECURE": "true"
}
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "clickhouse-official"],
"env": {
"CLICKHOUSE_HOST": "myhost.clickhouse.cloud",
"CLICKHOUSE_PORT": "8443",
"CLICKHOUSE_USER": "readonly",
"CLICKHOUSE_PASSWORD": "<your_password>",
"CLICKHOUSE_DATABASE": "default"
}
}
{
"command": "uvx",
"args": [
"--from",
"redis-mcp-server@latest",
"redis-mcp-server",
"--url",
"redis://localhost:6379/0"
]
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "redis-official"],
"env": {
"REDIS_URL": "redis://localhost:6379/0"
}
}
{
"command": "uvx",
"args": ["mcp-sqlite", "/path/to/your.sqlite"]
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "sqlite-panasenco"],
"env": {
"SQLITE_DB_PATH": "/path/to/your.sqlite"
}
}
{
"command": "dab",
"args": ["start", "--mcp-stdio"],
"cwd": "/path/to/your/dab-project"
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "mssql-azure-dab"],
"cwd": "/path/to/your/dab-project"
}
{
"command": "sql",
"args": ["-mcp"]
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "oracle-db-sqlcl"]
}
{
"command": "npx",
"args": [
"-y",
"mcp-remote",
"http://127.0.0.1:54321/mcp",
"--allow-http",
"--transport",
"http-only"
]
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "supabase-selfhost"]
}
{
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://mcp.supabase.com/mcp?read_only=true",
"--header",
"Authorization:Bearer YOUR_SBP_TOKEN",
"--transport",
"http-only"
]
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "supabase-cloud"],
"env": {
"SUPABASE_ACCESS_TOKEN": "sbp_your_token_here"
}
}
{
"command": "uvx",
"args": ["snowflake-labs-mcp", "--service-config-file", "/path/to/services.yaml"],
"env": {
"SNOWFLAKE_ACCOUNT": "ORG-ACCOUNT",
"SNOWFLAKE_USER": "your_user",
"SNOWFLAKE_PASSWORD": "<your_PAT>",
"SNOWFLAKE_ROLE": "JANUSCOPE_READONLY",
"SNOWFLAKE_WAREHOUSE": "COMPUTE_WH"
}
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "snowflake-labs"],
"env": {
"SNOWFLAKE_ACCOUNT": "ORG-ACCOUNT",
"SNOWFLAKE_USER": "your_user",
"SNOWFLAKE_PASSWORD": "<your_PAT>",
"SNOWFLAKE_ROLE": "JANUSCOPE_READONLY",
"SNOWFLAKE_WAREHOUSE": "COMPUTE_WH",
"SNOWFLAKE_MCP_CONFIG": "/path/to/services.yaml"
}
}
{
"command": "uvx",
"args": [
"awslabs.aurora-dsql-mcp-server@latest",
"--cluster_endpoint",
"<id>.dsql.eu-west-2.on.aws",
"--region",
"eu-west-2",
"--database_user",
"admin"
]
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "aurora-dsql"],
"env": {
"DSQL_CLUSTER_ENDPOINT": "<id>.dsql.eu-west-2.on.aws",
"AWS_REGION": "eu-west-2",
"DSQL_DATABASE_USER": "admin",
"AWS_PROFILE": "default"
}
}
{
"command": "uvx",
"args": ["awslabs.redshift-mcp-server@latest"],
"env": {
"AWS_REGION": "eu-west-2",
"AWS_PROFILE": "default"
}
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "redshift"],
"env": {
"AWS_REGION": "eu-west-2",
"AWS_PROFILE": "default"
}
}
{
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://mcp.neon.tech/mcp?readonly=true",
"--header",
"Authorization:Bearer YOUR_NAPI_TOKEN",
"--transport",
"http-only"
]
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "neon-cloud"],
"env": {
"NEON_API_KEY": "napi_your_token_here"
}
}
{
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Desktop"]
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "filesystem-mcp-official"],
"env": {
"FILESYSTEM_ALLOWED_DIR": "/Users/you/Desktop"
}
}
{
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "<your_PAT>"
}
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "github-official"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "<your_PAT>"
}
}
{
"command": "npx",
"args": ["-y", "@stripe/mcp"],
"env": {
"STRIPE_SECRET_KEY": "rk_live_<restricted_key>"
}
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "stripe-official"],
"env": {
"STRIPE_SECRET_KEY": "rk_live_<restricted_key>"
}
}
{
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.notion.com/mcp"]
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "notion-official"]
}
{
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.atlassian.com/v1/mcp"]
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "atlassian-official"]
}
{
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.linear.app/sse"]
}
{
"command": "npx",
"args": ["-y", "januscope", "--config", "linear-remote"]
}
Your favourite service / MCP isn't here? Open a lens-request issue so a maintainer or community contributor can pick it up. Or contribute one yourself: it's a single YAML file plus a short README.
About the env block. For most lenses the env block is byte-identical to what your vanilla setup had: JanuScope passes inherited env vars through unchanged. Three exceptions where the connection info moves from a positional argument into an env var (because the upstream MCP takes the value as
argv, and JanuScope's lens-spawning needs to read it from somewhere): Redis (REDIS_URL), SQLite (SQLITE_DB_PATH), Filesystem (FILESYSTEM_ALLOWED_DIR). The right-hand columns above show these changes.JanuScope needs only Node.js 20+; the wrapped MCP keeps its own requirements. Lenses using
uvxalso need uv. Three lenses wrap CLIs that need a one-time local install:dabformssql-azure-dab(dotnet tool install -g Microsoft.DataApiBuilder),sqlfororacle-db-sqlcl(Oracle SQLcl 25.4+ download), anddockerforgithub-official. Each per-lens README links the install step. In the Snowflake examples,JANUSCOPE_READONLYstands for a role you have provisioned with the required read-only access; see the Snowflake Lens prerequisites.Quick browse.
npx januscope lenses listlists every bundled lens;npx januscope lenses show <name>prints its full config + README.npx januscope lenses search <keyword>filters the catalogue.Setup diagnostic (optional).
npx -y januscope check --config <name>checks configuration, startup and tool discovery without calling upstream tools; schema-enabled Lenses also read database metadata. Run it with the same environment as your MCP client. Diagnostic details.
Option B: write your own policy
A minimal Postgres policy (~/januscope/postgres.yaml):
target:
command: uvx
args: ["postgres-mcp", "--access-mode=restricted"]
# No `env:` here. DATABASE_URI is supplied by the user via their
# MCP-client config (or shell env) and inherits through to the
# spawned target. The lens never renames operator env vars.
# Append policy text to every tool description the LLM sees.
instructions: |
READ-ONLY. SELECT only. Default LIMIT 100.
# Pre-inject the schema into the `execute_sql` tool description so the
# LLM can query without first discovering those tables.
dbSchema:
driver: postgres
connectionString: "${DATABASE_URI}"
tables: [orders, products, customers]
injectInto: [execute_sql]
# Reject known write patterns in SQL. Catches CTE-DML, SELECT INTO,
# pg_sleep, lo_export, etc.
sqlGuard:
tools: [execute_sql]
# Scrub matching PII from results and JSON-RPC error payloads.
redact:
rules:
- regex: '\b\d{3}-\d{2}-\d{4}\b' # US SSN
- field: "**.email"
# Audit log: one JSONL record per tracked call outcome.
audit:
sink: "~/mcp-audit.jsonl"
Point --config at the absolute path of your YAML, set DATABASE_URI in your client config's env block, restart the client. Done. The LLM now sees an execute_sql tool with your real schema baked into its description, has configured SQL write patterns and pg_sleep calls refused, sees matching SSNs and email fields as [REDACTED], and has tracked call outcomes recorded. Use backend permissions for a hard read-only or sensitive-column boundary; aliases and transformations can evade response rules. Sensitive-data controls.
The same policy pattern applies to non-database MCPs, drop dbSchema and sqlGuard, keep block / instructions / redact / audit. See the bundled Lenses in lenses/ for real examples covering GitHub, the filesystem, Stripe, Notion, Atlassian, and Linear.
Lens transparency rule. A lens never renames operator-supplied env vars and never declares them in
target.envjust to pass them through. The user sets the env var the upstream MCP itself reads, in their MCP-client config, and JanuScope inherits it. Only policy hardcodes (constants the lens decides for the user, likeALLOW_INSERT_OPERATION: "false"orCLICKHOUSE_SECURE: "true") belong intarget.env. Seelenses/CONTRIBUTING.mdfor the full rule.
Why JanuScope
Three problems that hit every real MCP deployment
-
Unsafe tools exposed by default. Most database and API MCPs ship write-capable tools (
execute_sql,create_table,delete_record). An LLM that sees the tool will eventually call it. Running it in production is a question of when, not if. -
LLMs fly blind. SQL MCPs expose a
querytool whose description says "run SQL." The LLM has no idea what tables exist, what columns they contain, or how they're related. It wastes 2-3 round-trips onlist_tables → describe_table → actual query, and often guesses wrong anyway. -
No audit trail. Nobody knows what the LLM asked yesterday. Nobody knows whether a PII column was read. Compliance teams reject the deployment.
Today's options, and what's wrong with them
Shortened here. Read the whole README on GitHub.
Signals
- GitHub stars
- 30
- Forks
- 5
- Last commit
- Sep 2026
- Weekly downloads
- 20
Advanced
- Delivery
- januscope MCP server → your ahel gateway (mcp.ahel.ai) → every connected AI client.
- Catalog kind
- mcp-server
- Gateway key
io-github-giancarloerra-januscope- Source
- github.com/giancarloerra/januscope