# LogCarverGuard, an MCP proxy between an AI agent and SQL Server

> Source: <https://dev.to/caiderek/logcarverguard-an-mcp-proxy-between-an-ai-agent-and-sql-server-143n>
> Published: 2026-11-05 00:48:10+00:00

I am a SQL Server DBA and I also write C# and .NET. Many people now let an AI coding agent like Claude Code or Cursor run SQL on a real database. I wanted something between the agent and the server, so I built LogCarverGuard. This post covers the free edition: what it blocks, how it decides, and what it does not do.

It is early. The version is 0.1.

The agent talks MCP to the proxy, and the proxy talks to SQL Server.

``` php
AI agent --(MCP, API key)--> LogCarverGuard proxy --(EXECUTE AS the caller's own login)--> SQL Server
```

There is one tool, `execute_sql`. For a `SELECT` you get the first 100 rows, and the rest is reported as a count. A write is executed and audited.

The proxy has one service login. Each person gets an API key, and the key maps to their own SQL login. For each request the proxy impersonates that login with `EXECUTE AS`, so the `GRANT` and `DENY` you already set for that person still apply. The proxy does not add any permission.

The service login does not need sysadmin. It needs `IMPERSONATE` on each user's login and a database user in the target database. I have integration tests for this on a real SQL Server: a key runs as its mapped login, the login can use what it was granted, and it is denied on a table it was never granted.

The checks use Microsoft's ScriptDom T-SQL parser. The guard looks at parsed statements, so text inside comments or strings is just text. A normal `UPDATE` with a comment like `-- TRUNCATE TABLE dbo.T` in front is not blocked. Same for a string literal that says "please truncate table later". Both are tested.

Some statements are always blocked: `DROP TABLE`, `DROP DATABASE`, `RESTORE DATABASE` and `RESTORE LOG`, `ALTER DATABASE ... SET`, `sp_executesql`, `xp_cmdshell`, and dynamic `EXEC` of a variable or a string. Identity switching is blocked too (`REVERT`, `EXECUTE AS`, `SETUSER`), and also `GRANT`, `DENY`, `REVOKE`, and the caller's own `BEGIN TRAN`, `COMMIT`, `ROLLBACK`, `SAVE TRANSACTION`.

Why `REVERT`: the proxy opens the connection as its service account and then switches to your login. If the agent sends `REVERT`, the session goes back to the service account and your grants no longer apply. The test case for this is `REVERT; UPDATE dbo.Payroll SET Salary = 0;` and it is blocked. SQL Server does not let the proxy lock the impersonation here, so the parser block is the only protection against this.

`TRUNCATE TABLE`, `ALTER TABLE ... DROP COLUMN`, and `EXEC` of a named stored procedure (including `INSERT ... EXEC`) are blocked unless the admin puts them on an allow-list. Entries are `schema.name`. If a statement names another database or a server, like `OtherDb.dbo.T` or `[srv].db.dbo.P`, it is never matched against the list.

The agent sends this batch:

```
UPDATE dbo.T SET Name = 'x' WHERE Id = 1;
TRUNCATE TABLE dbo.T;
```

The first line is OK. The second is a `TRUNCATE` on a table not on the allow-list. The guard checks every statement, so the whole batch is blocked, first line too. The agent gets the reason and the block goes into the audit log.

When a batch has `INSERT`, `UPDATE` or `DELETE`, the proxy runs it in its own transaction. If one statement fails, the whole batch is rolled back. DDL, `TRUNCATE` and `EXEC` run as they are and are not rolled back with the batch. Because the proxy owns the transaction, the caller cannot send `BEGIN TRAN` or `SET IMPLICIT_TRANSACTIONS ON`.

The audit log is a local SQLite file. It has who, when, the SQL, and blocked or executed. Executed statements are written after commit. If SQL Server itself rejects a statement, for example permission denied, it is not recorded.

There is also a local mode. Your AI tool starts it over stdio on your machine, with no server, Docker or API keys. Settings come only from environment variables. I tested it on Windows with integrated authentication.

With `LOGCARVERGUARD_READ_ONLY=true` it allows `SELECT` without `INTO`, plus `SET`, `DECLARE` and `PRINT`. Everything else is blocked with a reason. This includes `EXEC` even if allow-listed, `NEXT VALUE FOR`, and `OPENQUERY`, `OPENROWSET`, `OPENDATASOURCE`, because they can run a write on another server. SQL that does not parse is also blocked in read-only mode.

Local mode is a guardrail against accidents. It is not a security boundary. The connection string is on your machine and in your AI tool's config. An agent that can run shell commands or read that config can connect to SQL Server directly and skip the proxy. Every statement runs as the one login in the connection string.

The real security boundary is the grants on the SQL login. The blocked list is narrow on purpose. `DROP INDEX`, `sp_rename`, `BACKUP DATABASE` and a Linked Server `OPENQUERY` get no special handling. They run if the login has permission. Give each key an account with the least privilege you can live with, and never sysadmin or db_owner.

The free edition blocks. It does not undo a statement that already ran.

There is no mask-on-read. If the account can `SELECT` sensitive data, the agent can read it.

It only sees SQL that comes through this MCP connection. Files, APIs or a cloud console the agent can also use are outside it.

The API key is one static secret. If someone steals a key, they get that login's full access. The audit log has no tamper protection.

There are 200 test cases in five projects: Core, Server, Local, Cli, and the SQL Server integration tests. All passed on 2026-11-05. Most guard tests are parser-level unit tests. Only the integration and host tests use a real local SQL Server.

Some of the bypass cases tested:

`INSERT ... EXEC` variants, including `INSERT INTO dbo.T EXEC('DROP TABLE dbo.X')` and `INSERT dbo.T EXEC xp_cmdshell 'dir'`
`TRUNCATE` or `EXEC` with a database or server name, which must not match a local allow-list entry`xp_cmdshell` and `sp_executesql` stay blocked even when an admin adds them to the allow-list`SET IMPLICIT_TRANSACTIONS ON`
I wrote the tests myself. No one outside has done a penetration test. The tests show the rules work as written. They do not show the rules are complete.

The code and the full README are at [https://github.com/caiderek/LogCarverGuard](https://github.com/caiderek/LogCarverGuard). It is MIT licensed.

I wrote this from my own notes in Chinese and drafted the English with AI help, and I checked the technical claims against the tests in the repo.
