Repository navigation
feat(mcp): McpQueryService, read-only SQL core for MCP tools - #1139
Merged
Merged
Conversation
This was referenced Oct 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First part of the MCP epic #1133: the UI-free core the MCP tools will call (#1135 adds the server and the shim).
What's in it (
lib/core/mcp/)McpQueryService:listConnections,listTables,describeTable(columns, types, PK, FK target, indexes),sampleRows,runQuery,explainQueryfor PostgreSQL, MySQL, SQLite.ErdCatalog; no new execution code.McpToolExceptionwith a readable text (the model fixes its query).McpSqlGuard: one statement; only SELECT / WITH without DML / EXPLAIN without ANALYZE / SHOW / DESCRIBE / VALUES, plus SQLite schema pragmas. RefusesSELECT ... INTO(andINTO OUTFILE), row locks, and server functions that act even in a read-only transaction (pg_terminate_backend,pg_read_file,dblink,set_config,LOAD_FILE,load_extension, ...).createReadOnlyMcpDelegate: the editor delegates withisReadOnly: true, i.e. Postgresdefault_transaction_read_only, MySQLSET SESSION TRANSACTION READ ONLY, SQLiteSQLITE_OPEN_READONLY.McpAccessStore: opt-in per connection (ids inapp_settings), nothing shared by default. The settings UI comes in feat(mcp): Settings page, per-connection access and activity log #1136.listConnectionsreturns id, name, type, environment and database only; for SQLite only the file name.Tests (
test/core/mcp/)mcp_sql_guard_test.dart: allowed / refused statements incl. comments, strings, CTEs, multi-statements, EXPLAIN ANALYZE, SELECT INTO, dangerous functions, pragmas.mcp_query_service_test.dart: no credentials in output, opt-in access, refused SQL never reaches the delegate, limits and timeout, truncation, errors, schema tools, quoting, explain.mcp_sqlite_readonly_test.dart: real SQLite file through the production delegate; schema, sample, join, plan; an INSERT on the MCP session is refused by the database itself.Bug fix found on the way
ErdCatalogselectedm.nameandp.namewithout aliases. The SQLite driver returns rows as maps keyed by column name, so both collapsed, every catalog row lost a field and was dropped: the ERD diagram for SQLite connections was always empty. All catalog columns now have distinct aliases (also the Postgres FK query, for safety).Not done here: views are not listed yet (the catalog is base tables only).
Written without running locally; CI is the first run.
Closes #1134