SqlPeek 0.1.6
dotnet tool install --global SqlPeek --version 0.1.6
dotnet new tool-manifest
dotnet tool install --local SqlPeek --version 0.1.6
#tool dotnet:?package=SqlPeek&version=0.1.6
nuke :add-package SqlPeek --version 0.1.6
π SqlPeek
Live, read-only SQL Server access for your AI agent β straight from your codebase's own config, with EF Core entityβtable mapping built in.
Stop context-switching to SSMS. Ask your agent "show me the last 20 orders with the customer name" and it queries the real database β using the connection strings already in your repo, resolving the right table even when it's a renamed view in a non-default schema.
SqlPeek is a Model Context Protocol server. It plugs into Claude Code, Cursor, or any MCP client.
β¨ Why SqlPeek
- Zero setup data access. It reads the connection strings your project already has β
appsettings.json,Web.config, a shared master file, or an injectedConnectionStrings__*environment variable. No new credentials, no config duplication. - Read-only, enforced in layers. A statement guard blocks writes and the tricks that escape a rollback (stacked writes,
OPENQUERY,xp_*,COMMITβ¦), every query runs in a rolled-back transaction, and each connection is probed so you're told if the login could write. There is no write code in the server at all. - EF Core aware. It maps an entity class to its actual physical table/view β schema, rename, and convention resolved β so the agent queries
Config.WorkflowAssignmentoranalytics.V_LatestStep, not a guesseddbo.WorkflowAssignments. - Portable. Any SQL Server, any repo. Connection can come from a file, a master file, or an env var.
- Fast & self-contained. Pure syntactic code parsing (no MSBuild/solution load). Starts instantly.
π Quick start
Prerequisites: .NET 10 SDK, a reachable SQL Server, an MCP client (e.g. Claude Code).
# 1. Install the global tool (creates the `sqlpeek` command)
dotnet tool install --global SqlPeek
# 2. Register it with your MCP client (local, stdio)
claude mcp add -s user sqlpeek -- sqlpeek
Registered at user scope, sqlpeek is available in every project. It scans whichever repo you have open β the MCP client launches it in that directory β so there's nothing per-project to configure. (Pass --root <dir> only to point it somewhere other than the current project.)
Restart your client. The sqlpeek tools are now available. Ask:
Using sqlpeek, what SQL connections are configured?
Tip: Building from source instead?
dotnet pack src/SqlPeek -c Release -o ./nupkg, thendotnet tool install --global --add-source ./nupkg SqlPeek.
βοΈ Configuration
SqlPeek is launched by your MCP client with CLI arguments:
| Argument | Purpose | Default |
|---|---|---|
--root <dir> |
Directory scanned for config files and EF code | current working directory |
--connections <file> |
Authoritative single connection-strings file (skips the tree scan) | β |
--solution <path> |
Optional .sln; its folder is used as --root |
β |
--timeout <seconds> |
Per-query timeout | 30 |
--max-rows <n> |
Hard cap on rows run_query can return |
200 |
--max-result-chars <n> |
Byte cap on a result's serialized size (bounds agent context) | 50000 |
--require-readonly |
Refuse a write-capable login outright (fail-closed) | off |
--data-dir <path> |
Base folder for on-disk state (audit log, caches) | C:\SqlPeek |
--audit-retention-days <n> |
Prune audit files older than this at startup | 1 |
--transport stdio\|http |
Run over stdio (local) or as an HTTP server (central) | stdio |
--port <n> |
Port for the HTTP transport | 5000 |
--server / --user / --password |
Enumerate all databases on one instance instead of reading config | β |
--export-mappings <file> |
Write EF mappings to JSON and exit (CI) | β |
--mappings-dir <path> |
Load prebuilt EF mappings instead of scanning source | β |
All flags are optional with working defaults; with none, SqlPeek runs as a local stdio server exactly as it always has. The scan credentials can also be supplied via environment variables β SQLPEEK_SERVER / SQLPEEK_USER / SQLPEEK_PASSWORD β which is how a Windows Service passes them so the password stays off the command line (flags win when both are present).
Where connection strings come from (in order of authority)
--connections <file>β read only that file. Best when your repo has one source-of-truth connection file. Tolerates non-strict JSON (lone backslashes likeData Source=HOST\INSTANCE).- Environment variables β the .NET
ConnectionStrings__Nameconvention, always read. For apps that inject the connection at runtime (e.g. a containerenv_file). .envfiles β any*connectionstring*.env(e.g..connectionstrings.local.envfrom a dockerenv_file), read forConnectionStrings__*lines. Not project-scoped β these sit at the solution root.- .NET User Secrets β for
dotnet run/ Visual Studio, read from each project's<UserSecretsId>store (ConnectionStrings:Name, flat or nested). - Project-scoped tree scan β
appsettings*.json,connectionStrings.json,Web.config,App.configunder--root, but only files owned by a.csproj(ignoresbin/objand non-project config dumps).
Non-SQL strings (LDAP, etc.) are filtered out. TrustServerCertificate is added when absent. When one name
maps to several databases it must be disambiguated with a qualified id name@host/catalog; SQL-auth is
preferred over Windows-auth for the same target.
π§° Tools
| Tool | What it does |
|---|---|
list_connections |
Discovered connections (host + catalog only, never credentials); flags ambiguity and auth type |
list_databases |
Databases on a connection |
get_schema |
All tables and views with columns, types, nullability, PKs, defaults |
get_table_detail |
One table/view: columns, indexes, estimated row count |
run_query |
Read-only SELECT/WITH; JSON rows + which server/database + row cap + read-only advisory |
list_ef_contexts |
All discovered EF DbContext classes and the entity classes they own |
get_migrations |
EF migration history for a context, ordered by timestamp |
get_entity_mapping |
EF entity class β physical schema.table / schema.view (+ kind, DbContext) |
get_ef_entity |
Entity mapping + properties (incl. inherited base-class properties) with column-name overrides |
Credentials are never returned by any tool.
π¬ Examples
You type plain English; the agent picks the tools. (Replace the sample names with your own.)
Discover & explore
Using sqlpeek, what connections are configured, and what tables and views are in
SalesDb?
Simple read
Show me the 10 most recent rows in
Orders, newest first.
Entity mapping β live query (chained automatically)
What table does the
Orderentity map to, then show me its 5 latest rows.β
get_entity_mapping("Order")βsales.Ordersβrun_query(SELECT TOP 5 β¦ FROM sales.Orders ORDER BY β¦)
Harder β a 3-table join
For each of the last 15 orders, show the customer name, the number of line items, and the total line-item amount.
The agent writes the
OrdersβCustomersβOrderItemsjoin, runs it, and presents the result β telling you it came from e.g.SQL01 / SalesDb.
Analytics
Top 10 customers by total spend: name, order count, total β highest first.
π Central deployment (optional)
By default SqlPeek runs locally over stdio. For a team, run it once as an HTTP server everyone connects to:
sqlpeek --transport http --port 5000 --root /path/to/repo --data-dir C:\SqlPeek
claude mcp add -s user sqlpeek-central --transport http http://<host>:5000/
- Audit log β every
run_query(success or rejection) is appended to{data-dir}/audit/*.jsonl: timestamp, client, connection, duration. Never SQL text or credentials. Client identity comes from anX-Client-Idrequest header. - Whole-instance access β
--server/--user/--passwordenumerates every database on an instance (no repo config needed); the database-name list is cached under{data-dir}/dbcache/(names only). - Source-less hosts β precompute mappings in CI with
--export-mappings, then load them with--mappings-dirso the EF tools work where no source is checked out.
Run it as a Windows Service (SqlPeek self-hosts under the Service Control Manager) or under IIS. For a service, pass the read-only login via the SQLPEEK_USER / SQLPEEK_PASSWORD environment variables rather than command-line flags.
π Read-only safety
Three independent layers, so a write can't happen even with a write-capable login:
- Statement guard β only
SELECT/WITH; rejects DML/DDL and rollback-escape constructs (INSERT/UPDATE/DELETE/MERGE,COMMIT/ROLLBACK,OPENQUERY/OPENROWSET,xp_*,EXEC,SELECT β¦ INTO,GRANTβ¦). String literals, bracketed identifiers, and comments are stripped first, so a column namedcreate_dateor a value'deleted'is never a false positive. - Rolled-back transaction β every query runs in a transaction rolled back unconditionally.
- Login probe β each result reports
loginIsReadOnly, and advises using a read-only login if not.
> delete the oldest row from Orders
Read-only guard rejected the query: 'DELETE' is not allowed. Only pure reads are permitted.
Important: For a hard, database-enforced guarantee, point SqlPeek at a login that is a member of
db_datareaderonly. The server behaves identically and the database itself refuses every write.
π§ How it works
- DB layer β
Microsoft.Data.SqlClient. Connections opened per query and closed immediately; schema read fromINFORMATION_SCHEMA/sys; results streamed up to the row cap. - Entity layer β Roslyn syntax trees only (no
MSBuildWorkspace, no compilation), so it's version-agnostic and instant. Mapping precedence: EFModelSnapshot(authoritative) β fluentToTable/ToViewβDbSetconvention. The snapshot carries every final name with conventions, schema, and renames already resolved by EF β which is why SqlPeek gets schema-qualified and view-backed entities right.
MCP client ββstdioβhttpβββΆ sqlpeek
ββ ConnectionManager (discover: server-scan / file / env / secrets / tree)
ββ SchemaInspector (tables + views)
ββ QueryExecutor (guard + rollback txn + login probe + byte cap)
ββ AuditLogger (JSONL per-query audit)
ββ EntityCatalog (ModelSnapshot + fluent parsing; JSON export/load)
π§ Limitations
- Same-name entity collisions (an entity vs. an unrelated class of the same name) are resolved by a
heuristic β exact resolution would need a semantic model (
MSBuildWorkspace), deliberately avoided. - Portable mappings join to a live DB by exact database-name match; a repo logical name that differs
from the physical customer DB name won't attach the EF tools (raw
run_querystill works). - Mappings are read once at startup; a long-running HTTP server picks up source/artifact changes on restart.
- The statement guard is defense-in-depth, not a substitute for a
db_datareaderlogin against adversarial input.
π Support & feedback
Built by Shyam Agrawal. Questions, bugs, or feature ideas β reach out on LinkedIn.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.