Skip to content

Commit b14cab2

Browse files
committed
Make sql.query strictly read-only, improve tool descriptions
- sql.query now always enforces read-only mode regardless of server MODE - sql.query annotations: readOnlyHint=true, destructiveHint=false - Clarify all tool descriptions: read-only tools say so explicitly, mutating tools (db.apply, db.migrate) list supported statements - ChatGPT will no longer prompt for confirmation on SELECT queries
1 parent 6fad2a9 commit b14cab2

1 file changed

Lines changed: 39 additions & 15 deletions

File tree

app/mcp/tools.py

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def sql_query(payload: Dict[str, Any]) -> Dict[str, Any]:
1919
effective_limit = min(limit_override, config.limit_default)
2020
rows, columns, elapsed_ms, error = executor.execute(
2121
sql,
22-
mode=config.mode,
22+
mode="read-only",
2323
limit_default=effective_limit,
2424
timeout_ms=config.timeout_ms,
2525
)
@@ -378,10 +378,9 @@ def db_migrate_plan_apply(payload: Dict[str, Any]) -> Dict[str, Any]:
378378
name="sql.query",
379379
title="SQL Query",
380380
description=(
381-
"Execute a SQL query. "
382-
f"Server mode: {config.mode}. "
383-
"In read-only mode only SELECT/WITH/EXPLAIN are allowed. "
384-
"In execute mode DDL/DML are also permitted."
381+
"Execute a read-only SQL query. "
382+
"Only SELECT, WITH, and EXPLAIN statements are allowed. "
383+
"For INSERT, UPDATE, DELETE, or DDL statements use db.apply."
385384
),
386385
input_schema={
387386
"type": "object",
@@ -403,8 +402,8 @@ def db_migrate_plan_apply(payload: Dict[str, Any]) -> Dict[str, Any]:
403402
},
404403
},
405404
annotations=ToolAnnotations(
406-
read_only_hint=(config.mode == "read-only"),
407-
destructive_hint=(config.mode == "execute"),
405+
read_only_hint=True,
406+
idempotent_hint=True,
408407
),
409408
),
410409
sql_query,
@@ -413,7 +412,10 @@ def db_migrate_plan_apply(payload: Dict[str, Any]) -> Dict[str, Any]:
413412
ToolDef(
414413
name="sql.schema",
415414
title="SQL Schema",
416-
description="Inspect database schema (tables, columns, indexes).",
415+
description=(
416+
"Inspect database schema: tables, columns, types, indexes, "
417+
"and foreign keys. Read-only introspection, no data is modified."
418+
),
417419
input_schema={
418420
"type": "object",
419421
"properties": {"table": {"type": "string"}},
@@ -430,7 +432,10 @@ def db_migrate_plan_apply(payload: Dict[str, Any]) -> Dict[str, Any]:
430432
ToolDef(
431433
name="sql.explain",
432434
title="SQL Explain",
433-
description="Return an explain plan for a SQL query.",
435+
description=(
436+
"Return the EXPLAIN plan for a SQL query. "
437+
"Read-only, does not execute the query."
438+
),
434439
input_schema={
435440
"type": "object",
436441
"properties": {"sql": {"type": "string"}},
@@ -453,7 +458,10 @@ def db_migrate_plan_apply(payload: Dict[str, Any]) -> Dict[str, Any]:
453458
ToolDef(
454459
name="db.design",
455460
title="DB Design",
456-
description="Return a desired schema JSON template for design workflows.",
461+
description=(
462+
"Return a desired schema JSON template for design workflows. "
463+
"Read-only, provides a starting template for db.schema.diff."
464+
),
457465
input_schema={
458466
"type": "object",
459467
"properties": {"domain": {"type": "string"}},
@@ -475,7 +483,10 @@ def db_migrate_plan_apply(payload: Dict[str, Any]) -> Dict[str, Any]:
475483
ToolDef(
476484
name="db.schema.diff",
477485
title="DB Schema Diff",
478-
description="Compare desired schema JSON with current database schema.",
486+
description=(
487+
"Compare desired schema JSON with current database schema. "
488+
"Read-only diff showing missing/extra tables, columns, and indexes."
489+
),
479490
input_schema={
480491
"type": "object",
481492
"properties": {"desired_schema": {"type": "object"}},
@@ -503,7 +514,10 @@ def db_migrate_plan_apply(payload: Dict[str, Any]) -> Dict[str, Any]:
503514
ToolDef(
504515
name="db.migrate.plan",
505516
title="DB Migrate Plan",
506-
description="Generate SQL migration plan from desired schema.",
517+
description=(
518+
"Generate SQL migration statements from desired schema diff. "
519+
"Read-only planning step, does not execute any statements."
520+
),
507521
input_schema={
508522
"type": "object",
509523
"properties": {
@@ -531,7 +545,11 @@ def db_migrate_plan_apply(payload: Dict[str, Any]) -> Dict[str, Any]:
531545
ToolDef(
532546
name="db.apply",
533547
title="DB Apply",
534-
description="Apply a single SQL statement (DDL/DML) in execute mode.",
548+
description=(
549+
"Execute a single mutating SQL statement that changes database state. "
550+
"Supports INSERT, UPDATE, DELETE, and DDL (CREATE, ALTER, DROP). "
551+
"Requires server MODE=execute. For read-only queries use sql.query."
552+
),
535553
input_schema={
536554
"type": "object",
537555
"properties": {"sql": {"type": "string"}},
@@ -553,7 +571,10 @@ def db_migrate_plan_apply(payload: Dict[str, Any]) -> Dict[str, Any]:
553571
ToolDef(
554572
name="db.migrate",
555573
title="DB Migrate",
556-
description="Apply a batch of SQL statements with optional dry-run.",
574+
description=(
575+
"Apply a batch of mutating SQL statements with optional dry-run. "
576+
"Requires server MODE=execute."
577+
),
557578
input_schema={
558579
"type": "object",
559580
"properties": {
@@ -579,7 +600,10 @@ def db_migrate_plan_apply(payload: Dict[str, Any]) -> Dict[str, Any]:
579600
ToolDef(
580601
name="db.migrate.plan_apply",
581602
title="DB Migrate Plan Apply",
582-
description="Generate a plan from desired schema and apply it.",
603+
description=(
604+
"Generate a migration plan from desired schema and apply it. "
605+
"Combines db.migrate.plan + db.migrate. Requires server MODE=execute."
606+
),
583607
input_schema={
584608
"type": "object",
585609
"properties": {

0 commit comments

Comments
 (0)