MCP transport: the trust boundary, stated
Starting a server is a decision about trust, and a tool that is itself about database safety owes you that decision in writing rather than in its release notes. This page is the boundary: what SQLens assumes, what it therefore does not protect you from, and — for every claim on this page — the test that keeps it true.
The server is your agent's child process
sqlens:mcp is started by your agent, as a child process, under your user, inheriting your
environment. That includes your database credentials, because that is what an environment is.
There is no authentication layer, and the reason is not that one was skipped. There is no second channel to authenticate. The only way to reach this server is to be the process that started it — and anything able to start it already runs as you, with your environment, and could open those same connections directly without SQLens' help.
An authentication step here would guard a door that is already inside the house. Worse, it would suggest a protection that is not there.
Therefore: your agent's MCP configuration is the access control
That sentence is the whole practical consequence, and it is worth reading twice. SQLens adds nothing to it and takes nothing away. If your agent can start this server, your agent can ask it everything the four default tools answer — about the database your application is configured against.
What SQLens does control is what those tools are able to do:
- they read; none of the default four writes anything, anywhere;
- the two that touch a database are off, and each is enabled on its own, by name;
- no tool accepts free-text SQL, a connection string, a host, a user or a password — every parameter is a bounded value or a name your application already configured.
Those are properties of the tools, not of the channel. They hold whoever is at the other end.
What is not there, deliberately
- No listener and no port. Nothing binds, so nothing is reachable from anywhere else on your machine or your network.
- No authentication layer. See above: there is no second channel to authenticate.
- No tenancy model. One server answers about one application's configured connections.
- No telemetry, and no switch to turn telemetry off — an opt-out is a confession that something is being sent.
The channel split you can rely on
stdout belongs to the protocol and carries nothing else. Every diagnostic — a startup banner, a
warning, the reason a run stopped — goes to stderr.
This matters more than it sounds: an MCP client parses stdout as a frame stream, and one stray
line from anywhere in the host application corrupts the session. SQLens engages a shield around
the run for exactly that reason, so a noisy service provider in your application cannot break
your agent's connection.
HTTP is out of scope for this version
Decided when the transport was chosen, and recorded rather than re-argued here: this version speaks stdio and nothing else.
An HTTP server is a shared endpoint. It needs authentication, authorization, transport security and an answer to "who may ask what" — an entire surface, and one that is worse half-built than absent. stdio needs none of it, because it inherits the trust boundary of the process that started it.
If HTTP ever arrives it is a separate decision with a threat model of its own, and the
transport setting exists so that day is a value in a configuration file rather than a new shape.
Every assumption above, and the test that holds it
A written assumption decays at the first refactoring unless something fails when it stops being true. Each of these is checked on every run:
| Assumption | Held by |
|---|---|
| Nothing binds a socket or a port; the transport is stdio only | tests/Unit/Agent/SqlensStdioTransportTest.php |
The configuration accepts no transport but stdio | tests/Unit/Agent/McpConfigTest.php |
stdout carries frames and nothing else, in a real process | tests/Feature/Agent/McpStdioSubprocessTest.php |
A noisy host application cannot reach stdout | tests/Unit/Agent/StdoutShieldTest.php |
| No credential value leaves the server, over a whole session | tests/Feature/Agent/McpOutputHygieneTest.php |
| No tool call reaches the network, and none triggers a refresh | tests/Feature/Agent/McpNoImplicitNetworkTest.php |
| No tool accepts free-text SQL or a connection string | tests/Feature/Architecture/McpParameterAllowlistTest.php |
| The agent layer holds no engine logic and no rights of its own | tests/Feature/Architecture/AgentLayerArchTest.php |
| Enabling one database-touching tool takes no other along | tests/Feature/Agent/MutatingOptInNonEscalationTest.php |
| A hostile parameter value never reaches a database | tests/Postgres/Agent/McpAbuseTest.php |
| The server survives a client's socket read timeout | tests/Idle/McpSocketpairIdleTest.php |
If a row here names a file that does not exist, the documentation test beside this page fails. That coupling is the point: a boundary nobody can verify is a boundary nobody should believe.