One Desktop App Instead of 100+ MCP Server Setups
Giving an AI assistant access to one database is straightforward. Giving it useful access to an application stack raises a more interesting question: who decides what it is allowed to do? A checkout investigation might
Giving an AI assistant access to one database is straightforward. Giving it useful access to an application stack raises a more interesting question: who decides what it is allowed to do?
A checkout investigation might span MySQL, Redis, MongoDB, Kubernetes, OpenSearch and InfluxDB. I want the assistant to compare evidence across those systems. I also want to see the exact command before it changes anything, keep service credentials out of its configuration, and inspect what actually ran.
That is the workflow behind GateRook: a desktop application that puts service connections behind a local MCP gateway, with scoped endpoints, access policies, approvals and an Activity history.
Disclosure: GateRook is my project. This article adapts the original walkthrough for developers who want to understand the execution path and its boundaries. The application source is on GitHub.
What โ100+ MCP serversโ means here
The number refers to integration coverage. As of November 15, 2026, the integration catalog contains 122 entries: 50 connector types, 71 compatible-service entries and one generic external MCP integration.
Several compatible services share a connector. A managed PostgreSQL service, for example, does not need a separate implementation of the PostgreSQL protocol.
For supported operations, I can configure a connection in GateRook instead of finding, installing and maintaining another service-specific MCP server. This does not mean I previously ran 122 servers, or that GateRook implements every feature of every specialist integration. Existing MCP servers can also sit behind the gateway when their tools are needed.
Most connectors are included. Cassandra, Neo4j, ClickHouse, DuckDB, H2 and Microsoft Access use optional modules and an optional Java runtime; their first download needs internet access. The connector delivery documentation explains the split.
One endpoint, several services, explicit permissions
There are three useful concepts:
- A connection identifies a service, its credentials and its access constraints.
- An endpoint exposes a selected set of connections and tools to an AI client.
- A policy determines how a requested operation may proceed.
For a checkout investigation, the arrangement looks like this:
AI client
|
| MCP
v
GateRook endpoint: shop-investigation
|
+-- authentication and endpoint scope
+-- argument validation and operation classification
+-- policy decision / approval when required
+-- execution and Activity record
|
+-- MySQL orders
+-- Redis cached availability
+-- MongoDB product catalog
+-- Kubernetes workloads and logs
+-- OpenSearch search documents
+-- InfluxDB metrics
The client gets access to the endpoint. GateRook supplies the credentials for the selected service connection. This removes database passwords from the assistant's tool arguments while keeping the actual database and cloud permissions in force.
Connections from the original controlled demo. It uses fictional shop data, five real local services and a local Kubernetes API fixture representing the GKE workflow. The loopback addresses and simplified development authentication are demo settings.
I can give a development assistant and a production investigation assistant different endpoints. Neither needs the entire integration catalog simply because the application supports it.
A prompt is useful; an enforced policy is the boundary
โPlease do not modify productionโ is a reasonable instruction to an assistant. I also want that intent represented in the execution layer.
GateRook has four tool modes:
| Mode | Behavior |
|---|---|
| Off | The tool is unavailable through that endpoint. |
| Read only | Classified reads can run; classified mutations go through approval. |
| Approval | Both reads and changes require review. |
| Bypass | Calls can run without approval; other applicable controls remain. |
The distinction between a Read only tool mode and a read-only endpoint matters. A read-only endpoint prohibits mutations and Bypass. For an investigation that must never write, that is the stronger boundary.
The original demo uses 17 read-only tools, two approval tools and 11 disabled tools, with no Bypass. Its Redis key restrictions also exclude session:*.
These controls complement service permissions. A narrowly scoped database user or cloud role still matters, especially when a connector's read-only enforcement depends on the service or its operation classifier. The security documentation describes those details.
Follow one SKU across the stack
Consider order 1001 and product sku-42: the product appears in search, but checkout reports it as unavailable.
A focused investigation request might be:
Compare order 1001 with the cached availability, product document, search document, checkout logs and recent metrics for sku-42. Return the evidence and a proposed fix. Do not make changes yet.
The original demo produces the following evidence:
| Tool | Observation |
|---|---|
sql_query |
Order 1001 is pending and uses inventory version 18. |
redis_get |
The cached product reports zero available units and version 17. |
mongo_find |
The catalog reports 12 units and version 18. |
opensearch_query |
The search document also reports 12 units and version 18. |
k8s_get / k8s_logs
|
The Kubernetes fixture returns a running pod and a sample cache-version mismatch. |
influxdb_query |
Seeded checkout metrics provide latency and error context. |
The SQL read is deliberately small:
SELECT id, status, inventory_version, total
FROM orders
WHERE id = 1001;
The mismatch suggests stale cache data. It does not prove that the cache caused every checkout error. In a real investigation, I would also check timestamps, the cache update path and subsequent behavior.
What changes operationally is that all these reads pass through one configured access point and appear in one history.
Approve the command that will actually run
The proposed fix in the demo updates one existing Redis key to inventory version 18 and gives it a five-minute expiry.
Here is the Redis command, formatted for readability:
SET "product:sku-42" '{"sku":"sku-42","available":12,"inventory_version":18}' XX PX 300000
There are two details worth reviewing before approval:
-
XXrequires the key to exist. It does not compare its previous value and is not compare-and-swap. -
PX 300000sets an explicit five-minute expiry, so the replacement does not accidentally become permanent.
In a real application, I would prefer its supported cache-refresh operation if that operation performs additional consistency checks.
The approval is attached to a concrete command and target, with a choice to deny or execute it.
Under the hood, GateRook stores the prepared action and its digest. Before execution, it rechecks the approval, expiry, applicable policy and action integrity. The core executes the stored payload, so the client cannot replace the reviewed command with another one after approval.
This is the control I care about: reviewing โset this key to this value with this expiryโ before the write reaches Redis. A follow-up read in the demo confirmed 12 available units, version 18 and a remaining expiry.
Keep the execution history next to the controls
An assistant's summary is useful, but I also want the underlying calls.
The original demo recorded ten calls across six connections: eight allowed reads, one approved write and one policy-denied read.
The denied request tried to read session:demo-user. The session:* restriction blocked it before the Redis operation ran. That is a useful test of the boundary: a normal product lookup succeeds while a session-key lookup fails.
Activity brings together the tool, connection, caller, decision, duration and execution outcome. For an approved action, I can inspect the review and whether execution matched the approved payload. These records establish what happened at the tool layer; they do not establish that the model's diagnosis was correct.
A large catalog should not become a giant prompt
There is another scaling problem: loading every tool definition into an assistant's context.
GateRook's automatic discovery mode switches to searched tool lists when the endpoint exceeds 40 tools or 100 KB of definitions. It exposes the core tools and ten most-used tools, while the rest remain discoverable through find_tools and callable through call_tool or call_read_tool.
Those wrappers preserve the underlying tool's policies, approval requirements, limits, masking and audit behavior. Discovery changes how a tool is found, not its permission boundary. The architecture documentation explains the mechanism.
What control means in practice
My goal is to control access, review changes and retain evidence. The scope of that control is explicit:
- GateRook governs calls routed through it. Separate shell access or another MCP connection needs its own controls.
- Keeping service credentials local does not keep all query results local. Allowed results reach the AI client and may reach its model provider.
- Approval is only useful if the proposed action is understood. It cannot make an incorrect fix correct.
For a first setup, I would choose one non-production workflow, add only its required connections, verify an allowed read and a denied request, then test approval for one narrowly scoped change. The getting-started guide covers setup, and the AI app guides cover client connections.
For me, the benefit of one desktop app is being able to answer three questions in one place: what can the assistant reach, what am I approving, and what actually ran?
The original article contains the full walkthrough. You can explore GateRook or inspect the source on GitHub.
How do you currently separate an AI assistant's permission to investigate from its permission to change infrastructure?
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes โ full credit and traffic to the original publisher.

