{"slug": "logcarverguard-an-mcp-proxy-between-an-ai-agent-and-sql-server", "title": "LogCarverGuard, an MCP proxy between an AI agent and SQL Server", "summary": "A SQL Server DBA and .NET developer built LogCarverGuard, an early-stage (v0.1) MCP proxy that sits between AI coding agents such as Claude Code or Cursor and a SQL Server instance. The proxy exposes a single execute_sql tool, impersonates each caller's own SQL login via EXECUTE AS so existing GRANT/DENY permissions still apply, and parses T-SQL with Microsoft's ScriptDom to block destructive or privilege-changing statements like DROP TABLE, REVERT, and dynamic EXEC, with an allow-list for TRUNCATE and named stored procedures. It also wraps INSERT/UPDATE/DELETE batches in its own transaction and logs executed or blocked statements to a local SQLite audit file, and offers a stdio-based local mode with a read-only setting.", "body_md": "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.\n\nIt is early. The version is 0.1.\n\nThe agent talks MCP to the proxy, and the proxy talks to SQL Server.\n\n``` php\nAI agent --(MCP, API key)--> LogCarverGuard proxy --(EXECUTE AS the caller's own login)--> SQL Server\n```\n\nThere 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.\n\nThe 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.\n\nThe 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.\n\nThe 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.\n\nSome 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`.\n\nWhy `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.\n\n`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.\n\nThe agent sends this batch:\n\n```\nUPDATE dbo.T SET Name = 'x' WHERE Id = 1;\nTRUNCATE TABLE dbo.T;\n```\n\nThe 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.\n\nWhen 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`.\n\nThe 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.\n\nThere 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.\n\nWith `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.\n\nLocal 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.\n\nThe 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.\n\nThe free edition blocks. It does not undo a statement that already ran.\n\nThere is no mask-on-read. If the account can `SELECT` sensitive data, the agent can read it.\n\nIt only sees SQL that comes through this MCP connection. Files, APIs or a cloud console the agent can also use are outside it.\n\nThe 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.\n\nThere 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.\n\nSome of the bypass cases tested:\n\n`INSERT ... EXEC` variants, including `INSERT INTO dbo.T EXEC('DROP TABLE dbo.X')` and `INSERT dbo.T EXEC xp_cmdshell 'dir'`\n`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`\nI 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.\n\nThe code and the full README are at [https://github.com/caiderek/LogCarverGuard](https://github.com/caiderek/LogCarverGuard). It is MIT licensed.\n\nI 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.", "url": "https://wpnews.pro/news/logcarverguard-an-mcp-proxy-between-an-ai-agent-and-sql-server", "canonical_source": "https://dev.to/caiderek/logcarverguard-an-mcp-proxy-between-an-ai-agent-and-sql-server-143n", "published_at": "2026-11-05 00:48:10+00:00", "updated_at": "2026-11-05 00:52:13.506463+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "ai-tools", "developer-tools", "ai-safety"], "entities": ["LogCarverGuard", "SQL Server", "Microsoft", "ScriptDom", "Claude Code", "Cursor", "MCP"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/logcarverguard-an-mcp-proxy-between-an-ai-agent-and-sql-server", "markdown": "https://wpnews.pro/news/logcarverguard-an-mcp-proxy-between-an-ai-agent-and-sql-server.md", "text": "https://wpnews.pro/news/logcarverguard-an-mcp-proxy-between-an-ai-agent-and-sql-server.txt", "jsonld": "https://wpnews.pro/news/logcarverguard-an-mcp-proxy-between-an-ai-agent-and-sql-server.jsonld"}}