Gatehouse
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Gatehouseshow me the total value of open deals in EMEA"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Gatehouse
A read only MCP gateway you can put in front of a real business system, with an audit console. Python, official MCP SDK, FastAPI, SQLite, React, TypeScript, Tailwind. 129 tests, 84 of them security tests. Runs with one command, or in Docker.
Give Claude (or any AI assistant) safe, read only access to your CRM, with a key per assistant, least privilege scopes, rate limits, and an audit log that shows everything the AI did and everything it was refused.

What it does
You have a system full of customer data. You want an AI assistant to answer questions from it. You do not want the AI to change anything, see everything, or do anything you cannot review later. Gatehouse sits between the two:
The AI cannot write. There is no tool that writes, the database is opened read only three separate ways, and a test suite attacks it to prove the point.
Every assistant gets its own key. A key decides which tools it may call, which rows it may see (for example EMEA accounts only), which fields stay masked (contact emails and phone numbers, deal amounts), and how many calls per minute it may make.
Everything is logged. Time, key, tool, arguments, decision, the rule that made the decision, rows returned, fields masked, latency. Blocked calls explain themselves: Blocked: key 'support-bot' is not scoped for list_deals.
A console a non engineer can read. Overview, audit log, keys and permissions, the tool catalogue with a Try it runner, a live attack suite, and copy and paste setup for Claude Desktop and Claude Code.
Real MCP, not a mock. Built on the official
mcpPython SDK (2.x), Streamable HTTP with bearer authentication, plus stdio. The demo's audit log is produced by real MCP client sessions against the running server.
|
|
Calls over time, why calls were blocked, top tools and keys, latency. All computed from the audit table. | One row per key, and a tool by key permission matrix. Create and revoke work. |
|
|
The same call run as three keys: unmasked, masked, and refused. | 31 attacks (writes, SQL injection, scope escapes, floods, stolen keys), run live, with evidence. |
The demo data is a fictional company (Halden Supply Co.) with 120 accounts, 344 contacts, 215 deals, 137 tickets and 417 activities, generated by a seeded script. No real companies, people or email addresses.
Related MCP server: EspoCRM Assistant
Run it
Requirements: Python 3.11 or newer, Node.js 20 or newer.
make demoThat creates .venv, installs the Python and web dependencies, builds the console and starts everything on one port. Open http://localhost:8104.
On startup the demo creates seven access keys, then replays a scripted week of AI traffic (799 calls, 75 of them blocked) through the real /mcp endpoint using the SDK's own client. It takes about five seconds, and the console shows a banner while it runs. Reset demo in the sidebar does the same again, so the state is reproducible.
Other commands:
make test # 129 tests: security suite, MCP end to end, API, demo replay
make attack # run the live attack suite in the terminal
make serve # start without reinstalling or rebuilding
make shots # recapture the screenshots (demo must be running)Docker (a multi stage Dockerfile and a docker-compose.yml are included):
docker compose up --buildConnect Claude
The console's Connect screen builds these for you with a working demo key. For Claude Code:
claude mcp add --transport http gatehouse http://localhost:8104/mcp \
--header "Authorization: Bearer <access key>"For Claude Desktop, the Connect screen gives a claude_desktop_config.json block (using the mcp-remote bridge to send the key), and a stdio variant that needs no Node.js.
Configuration
Variable | Default | Meaning |
|
| Port for the MCP endpoint, API and console |
|
| Bind address |
|
|
|
| unset | When set, the console and |
|
| The URL printed on the Connect screen |
| unset | Extra |
|
| Where |
| unset | The access key for the stdio transport |
No API keys, no paid services, no network access needed.
Engineering notes
The path of one tool call
Every call, from either transport or from the console's Try it runner, goes through Gateway.call (gatehouse/gateway.py). Checks run in this order and the first failure wins:
Authentication. Bearer token, looked up by SHA-256 hash. Unknown, malformed and revoked keys get HTTP 401 before any MCP message is read. Revocation also cuts off sessions that are already open.
Rate limit. Sliding 60 second window per key. Refused calls still count, so probing is throttled too.
Tool exists. Eight published tools, nothing else. There is no
run_sql.Tool scope. The key's allow list. A key is also only shown the tools it may call.
Argument validation. Strict Pydantic models: closed schema (unknown fields rejected), enums, id patterns, length and range caps, no type coercion.
Masked filters. A key that cannot see deal amounts cannot filter by
min_amounteither, because repeated filtered calls would binary search the hidden value. Contact search never looks at email or phone for the same reason.Row scope. Asking for another region, or for the id of a row in another region, is refused. The AI is told "not found", the administrator is told the truth.
Query. A constant SQL template on the read only connection, with the key's region scope inside the SQL.
Masking. Sensitive fields are redacted at any depth of the result, and the AI is told which fields are masked so it does not guess.
Whatever happens, one row is written to audit_log. Tokens are never logged, oversized arguments are truncated, and results are never stored, so the log cannot leak what a key could not see.
Read only, in depth
crm.sqliteis opened withmode=ro, thenPRAGMA query_only = ON, then an SQLite authorizer that vetoes every operation except reads (this also blocksATTACHandPRAGMAchanges). The tests remove layers and show each one holds alone.Every SQL statement is a module level constant in
gatehouse/tools.py. Arguments only ever travel as bound parameters. The one place SQL varies (GROUP BYcolumn inpipeline_summary) picks between three constants using an already validated enum.Keys and the audit log live in a different file (
control.sqlite). The tool connection has never opened it.The CRM file is
chmod 444after seeding, and a test hashes it before and after a hostile session.
The security suite
tests/test_security.py is written to be read top to bottom as a threat model: writes, SQL injection, tool scope, row scope, field masking, rate limits, authentication, the audit log. 84 cases. tests/test_mcp_end_to_end.py repeats the important ones over the real wire: it starts the app with uvicorn on a TCP port and connects with the SDK client over Streamable HTTP (and launches the stdio server as a subprocess). gatehouse/attacks.py is the 31 check suite behind the console's Security checks screen and make attack; one test sabotages masking to prove the suite reports failures rather than hiding them.
Honest numbers
The console has no hard coded figures. The Overview is a set of queries over audit_log (gatehouse/audit.py); percentiles are nearest rank over the latencies of served calls. The demo audit log is written by the gateway itself while gatehouse/demo.py drives real MCP sessions: several keys behaving differently, a support bot that keeps asking for deals it is not scoped for, an outside integrator that probes for SQL injection and later floods the endpoint, a revoked key that keeps knocking. The only thing injected is the clock, so a week of traffic can be produced in seconds. Latency is measured inside the gateway (policy checks plus query, not network), which is why it is well under a millisecond against local SQLite.
What this is not
The backing system is SQLite so the demo runs anywhere. Pointing the same gateway at Postgres or a SaaS API means replacing the query layer in tools.py and keeping a read only credential; the policy engine, keys, audit log and console do not change. The admin console has optional Basic auth, not SSO. The rate limiter is in memory, so it is per process.
Layout
gatehouse/
tools.py tool catalogue, strict input models, constant SQL templates, masking
gateway.py the decision pipeline and audit writer
keys.py access keys (hashed), scopes, revocation
db.py read only CRM connection, control database
mcp_server.py MCP server: Streamable HTTP with bearer auth, and stdio
api.py, app.py admin API, static console, one process on one port
audit.py audit queries and overview statistics
demo.py demo keys and the scripted week replayed over real MCP sessions
attacks.py the live attack suite
crm_seed.py deterministic fictional CRM
web/ React 19, TypeScript, Vite, Tailwind CSS v4
tests/ security suite, MCP end to end, API, demo replayMIT licensed.
This server cannot be deployed
Maintenance
Related MCP Connectors
Safe, read-only Postgres and MySQL access for AI agents. Audit log + column-level controls.
The HubSpot MCP Server acts as a bridge that enables AI assistants and Large Language Models to securely interact with HubSpot CRM data through natural conversation, without requiring users to understand complex API structures. It provides read-only access to standard CRM objects (contacts, companies, deals, tickets, products, invoices, and more) and their associations, secured via OAuth 2.0, allowing AI agents to perform tasks like summarizing deals, fetching company updates, and looking up record changes.
- HAVNOAuthapp.havnre
Read-only AI access to HAVN properties, leads, tasks, files, media, and analytics.
- OsboonOAuthcom.osboon
Read-only AI access to Osboon business card analytics, viewers, links, connections and contacts.
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceProvides AI assistants with read-only access to Secureframe's compliance data, enabling querying of security controls, tests, users, vendors, and more across frameworks like SOC 2 and ISO 27001.8MIT
- FlicenseBqualityDmaintenanceProvides read-only EspoCRM access for AI assistants, enabling record retrieval and preparation of signed change sets without direct modification or deletion.7-
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to interact with a CRM covering companies, people, leads, deals, and more, with role checks, scoped agent keys, approval gates, and a shared audit trail.AGPL 3.0
- AlicenseAqualityCmaintenanceA read-only MCP server that enables AI assistants to query Freshsales CRM data (leads, contacts, deals, accounts) via search, saved views, and record lookup.5MIT



