Skip to content

Commit 559a25c

Browse files
authored
feat(aurora-dsql): mirror dsql_lint skill content and Kiro Power translation (awslabs#3423)
Mirror the merged agent-plugins skill content (awslabs/agent-plugins#157) to the MCP repo standalone skill and Kiro Power. Skill (skills/dsql-skill/): - Update SKILL.md: add dsql_lint to MCP Tools, update Workflows 2/6, rename Workflow 7 to 'Validate and Migrate to DSQL' - Add references/dsql-lint.md: tool API, workflow steps, ORM guidance, unfixable error resolution, error handling - Sync all 5 alias SKILL.md files Kiro Power (kiro_power/): - Update POWER.md: add dsql-lint steering entry, add dsql_lint tool, update Workflows 2/6/7 - Add steering/dsql-lint.md (same content as references/) Paired with awslabs/agent-plugins#157 (canonical source, merged) and awslabs#3350 (server tool, merged).
1 parent 36b5be8 commit 559a25c

9 files changed

Lines changed: 487 additions & 259 deletions

File tree

src/aurora-dsql-mcp-server/kiro_power/POWER.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ This power includes the following steering files in [steering](./steering)
8282
- Load when setting up connection pooling or connectivity tools
8383
- **auth-scaling**
8484
- Load when planning connection scaling patterns
85+
- **dsql-lint**
86+
- SHOULD load when validating SQL for DSQL compatibility or migrating schemas
87+
- `dsql_lint` MCP tool reference, fix statuses, workflow steps, ORM integration, unfixable error resolution, error handling
8588

8689
---
8790

@@ -94,6 +97,9 @@ The `aurora-dsql` MCP server provides these tools:
9497
2. **transact** - Execute DDL/DML statements in transaction (takes list of SQL statements)
9598
3. **get_schema** - Get table structure for a specific table
9699

100+
**SQL Validation:**
101+
4. **dsql_lint** - Validate SQL for DSQL compatibility and optionally auto-fix issues. Use before executing externally-sourced SQL.
102+
97103
**Documentation & Knowledge:**
98104
4. **dsql_search_documentation** - Search Aurora DSQL documentation
99105
5. **dsql_read_documentation** - Read specific documentation pages
@@ -170,11 +176,13 @@ Authorize the caller against the tenant **before** validating format or calling
170176

171177
### Workflow 2: Safe Data Migration
172178

173-
1. Add column using `transact`: `transact(["ALTER TABLE ... ADD COLUMN ..."])`
174-
2. Populate existing rows with UPDATE in separate `transact` calls (batched under 3,000 rows)
175-
3. Verify migration with `readonly_query` using COUNT
176-
4. Create async index for new column using `transact` if needed
179+
1. Validate DDL with `dsql_lint(sql=..., fix=true)` — apply fixes if needed
180+
2. Add column using `transact`: `transact(["ALTER TABLE ... ADD COLUMN ..."])`
181+
3. Populate existing rows with UPDATE in separate `transact` calls (batched under 3,000 rows)
182+
4. Verify migration with `readonly_query` using COUNT
183+
5. Create async index for new column using `transact` if needed
177184

185+
- **MUST** validate DDL with `dsql_lint` before executing
178186
- **MUST** add column first, populate later
179187
- **MUST** issue ADD COLUMN with only name and type; apply DEFAULT via separate UPDATE
180188
- **MUST** batch updates under 3,000 rows in separate `transact` calls
@@ -199,13 +207,13 @@ Authorize the caller against the tenant **before** validating format or calling
199207

200208
### Workflow 6: Table Recreation DDL Migration
201209

202-
DSQL does NOT support direct `ALTER COLUMN TYPE`, `DROP COLUMN`, `DROP CONSTRAINT`, or `MODIFY PRIMARY KEY`. These operations require the **Table Recreation Pattern**.
210+
DSQL does NOT support direct `ALTER COLUMN TYPE`, `DROP COLUMN`, `DROP CONSTRAINT`, or `MODIFY PRIMARY KEY`. These require the **Table Recreation Pattern**. This is a destructive workflow that requires user confirmation at each step. Validate the new CREATE TABLE with `dsql_lint(sql=..., fix=true)` before execution.
203211

204212
**MUST** load [ddl-migrations-overview.md](steering/ddl-migrations-overview.md) before attempting any of these operations.
205213

206-
### Workflow 7: MySQL to DSQL Schema Migration
214+
### Workflow 7: Validate and Migrate to DSQL
207215

208-
**MUST** load [mysql-type-mapping.md](steering/mysql-type-mapping.md) for type mappings, feature alternatives, and migration steps.
216+
Run `dsql_lint(sql=source_sql, fix=true)` to validate and auto-convert PostgreSQL-compatible SQL. For MySQL-specific syntax (SET, ENGINE, PARTITION BY), `dsql_lint` returns a parse error — fall back to [mysql-type-mapping.md](steering/mysql-type-mapping.md) for manual conversion. **MUST** load [dsql-lint.md](steering/dsql-lint.md) for the full workflow, ORM-specific guidance, and unfixable error resolution.
209217

210218
### Workflow 8: Query Plan Explainability
211219

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# DSQL Lint — SQL Compatibility Validation
2+
3+
`dsql_lint` is an MCP tool that validates SQL for Aurora DSQL compatibility and auto-fixes
4+
common issues. It provides deterministic, rule-based analysis — more reliable than heuristic
5+
reasoning for catching DSQL-specific constraints.
6+
7+
---
8+
9+
## MCP Tool Reference
10+
11+
### dsql_lint
12+
13+
| Parameter | Type | Required | Description |
14+
| --------- | ------- | -------- | ------------------------------------------------- |
15+
| `sql` | string | Yes | SQL to validate (max 1,000,000 characters) |
16+
| `fix` | boolean | No | Return DSQL-compatible fixed SQL (default: false) |
17+
18+
Server timeout: 30 seconds per call.
19+
20+
**Returns:**
21+
22+
Concrete example (from `dsql_lint(sql="CREATE INDEX idx ON t (c);", fix=true)`):
23+
24+
```json
25+
{
26+
"diagnostics": [
27+
{
28+
"rule": "index_async",
29+
"line": 1,
30+
"message": "CREATE INDEX without ASYNC is not supported in DSQL. Index: idx",
31+
"suggestion": "Use `CREATE INDEX ASYNC ...` instead.",
32+
"fix_result": { "status": "fixed", "detail": "Added ASYNC keyword to CREATE INDEX" },
33+
"statement_preview": "CREATE INDEX idx ON t (c);"
34+
}
35+
],
36+
"fixed_sql": "CREATE INDEX ASYNC idx ON t (c);\n",
37+
"summary": { "errors": 0, "warnings": 0, "fixed": 1 }
38+
}
39+
```
40+
41+
**Schema notes:**
42+
43+
- `rule` is a snake_case string identifying the rule (e.g., `index_async`, `truncate`, `json_type`, `set_transaction`); `line` is 1-indexed.
44+
- `fix_result.status` is one of three values: `fixed`, `fixed_with_warning`, or `unfixable`. Always check this field — `fix_result` is present for every diagnostic when `fix=true`.
45+
- `fix_result.detail` is present for `fixed` and `fixed_with_warning`; absent for `unfixable`.
46+
- `fixed_sql` is always a string when `fix=true` (may include the original text verbatim for `unfixable` portions that could not be rewritten); `null` when `fix=false`. Presence of `fixed_sql` does NOT mean the SQL is safe to execute — check every diagnostic first.
47+
- `summary.errors` counts `unfixable` diagnostics; `summary.warnings` counts `fixed_with_warning`; `summary.fixed` counts `fixed`.
48+
- `statement_preview` is the linter's pointer to the offending statement — useful when presenting diagnostics to the user.
49+
50+
---
51+
52+
## Fix Result Statuses
53+
54+
| `fix_result.status` | Meaning | Agent action |
55+
| -------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
56+
| `fixed` | Safe mechanical transformation | Accept; for destructive DDL (`DROP`, `RENAME`, `TRUNCATE`) confirm with user before executing |
57+
| `fixed_with_warning` | Fix applied, may need app-layer changes | Present to user, explain implications, obtain acknowledgement before executing |
58+
| `unfixable` | Cannot auto-fix | Present to user with a proposed rewrite from the Unfixable Errors table, obtain confirmation before substituting |
59+
60+
---
61+
62+
## Workflow: Validate & Migrate SQL to DSQL
63+
64+
Use for any SQL that was not composed by the agent itself from skill knowledge — including user-pasted SQL, migration files, ORM output (Django, Rails, Prisma, TypeORM, Sequelize, SQLAlchemy), pg_dump exports, and hand-written schemas. Applies to DDL and schema-mutating DML; do **not** lint ad-hoc read-only `SELECT`s.
65+
66+
1. Obtain source SQL from user (migration file, ORM output, schema dump, or inline SQL). `dsql_lint` accepts multi-statement SQL in a single call — pass the whole batch.
67+
2. Run `dsql_lint(sql=source_sql, fix=true)`. Default to `fix=true` for any migration scenario; use `fix=false` only when the user explicitly asked for validation-only output, or when re-verifying manually rewritten SQL.
68+
3. For each diagnostic, emit a user-visible bullet showing `rule`, `message`, `suggestion`, `statement_preview`, and `fix_result.status`. Handle per the Fix Result Statuses table: `fixed` applies automatically (confirm for destructive DDL); `fixed_with_warning` needs user acknowledgement; `unfixable` needs user confirmation of a proposed rewrite.
69+
4. If **any** diagnostic is `unfixable`, do NOT execute the returned `fixed_sql` — it still contains the unfixable portion verbatim. Collect user-confirmed rewrites from the Unfixable Errors table, merge them into the SQL, then re-run `dsql_lint(fix=true)` on the combined SQL to confirm it is clean.
70+
5. Also surface the `fixed_sql` body itself to the user before executing — prompt-injection can hide inside rewritten statements.
71+
6. Once diagnostics are resolved and the user has acknowledged, split the clean `fixed_sql` on statement boundaries.
72+
7. For destructive DDL (`DROP`, `RENAME`, `TRUNCATE`) confirm with the user before executing, matching Workflow 6's confirmation gate.
73+
8. Execute each DDL with `transact(["<single DDL statement>"])` — one DDL per call.
74+
9. Verify schema with `get_schema`.
75+
76+
**Critical rules:**
77+
78+
- **MUST** run `dsql_lint` on any externally-sourced SQL before executing it with `transact`.
79+
- **MUST** surface each diagnostic and the `fixed_sql` body to the user before executing.
80+
- **MUST NOT** execute `fixed_sql` while any diagnostic has `fix_result.status == "unfixable"` — resolve first, then re-lint until clean.
81+
- **MUST** re-run `dsql_lint` on manually rewritten SQL before executing it.
82+
- **MUST** issue each DDL in its own `transact` call.
83+
84+
**User override:** If the user explicitly declines validation ("just run it"), warn once that deterministic validation is being skipped and record the skip; proceed only when the user repeats the request.
85+
86+
**ORM-specific guidance:**
87+
88+
- **Django:** Run `python manage.py sqlmigrate <app> <migration>` to get raw SQL, then lint.
89+
- **Rails (6.1+):** Set `config.active_record.schema_format = :sql`, then run `rails db:schema:dump` (legacy `db:structure:dump` still works in older Rails). Lint the generated `db/structure.sql`.
90+
- **Prisma:** Use `prisma migrate diff --from-empty --to-schema-datamodel ./prisma/schema.prisma --script` to emit SQL to stdout, then lint.
91+
- **TypeORM/Sequelize:** Generate migration SQL to a file, then lint.
92+
- **SQLAlchemy:** Compile DDL without executing — e.g., `for table in metadata.tables.values(): print(CreateTable(table).compile(engine))`. Do **not** call `metadata.create_all(engine)` with a real engine — it executes the DDL before lint. Alternatively use `create_mock_engine` to capture DDL.
93+
94+
---
95+
96+
## Handling Unfixable Errors
97+
98+
When `dsql_lint` returns a diagnostic with `fix_result.status == "unfixable"`, **MUST** present the proposed rewrite to the user and obtain confirmation before substituting. Use skill knowledge to resolve:
99+
100+
Only diagnostics with `fix_result.status == "unfixable"` need user-confirmed rewrites — these are the most common:
101+
102+
| Rule | Resolution |
103+
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
104+
| `create_table_as` | CREATE TABLE with explicit columns, then `INSERT ... SELECT` |
105+
| `truncate` | Use `DELETE FROM table_name` (batch if > 3,000 rows) |
106+
| `unsupported_alter_table_op` | Use Table Recreation Pattern — see [ddl-migrations/overview.md](ddl-migrations/overview.md) and Workflow 6 |
107+
| `add_column_constraint` | ADD COLUMN with name + type only, then backfill via UPDATE. If NOT NULL/DEFAULT required, use Table Recreation Pattern. |
108+
| `index_expression` | Create a computed column, then index that column |
109+
| `index_partial` | Create a full index; filter at query time |
110+
| `set_transaction` | Omit — DSQL uses Repeatable Read (fixed); `SET TRANSACTION ISOLATION LEVEL` is not supported |
111+
112+
Other rules such as `temp_table`, `inherits`, `index_using`, and `transaction_isolation` are emitted as `fixed` or `fixed_with_warning` — follow the Fix Result Statuses table rather than rewriting manually.
113+
114+
---
115+
116+
## Error Handling
117+
118+
If `dsql_lint` is unavailable, returns a parse error, or times out:
119+
120+
- **MCP unavailable:** Inform the user that deterministic validation is unavailable and ask whether to (a) retry later or (b) proceed with manual validation using [development-guide.md](development-guide.md) DDL rules and type constraints. Proceed only on explicit user confirmation — the MUST-validate gate is not silently bypassed.
121+
- **Parse error (`parse_error` rule):** The SQL contains syntax the PostgreSQL parser cannot handle (MySQL-specific dialect, malformed SQL, etc.). Fall back to [mysql-migrations/type-mapping.md](mysql-migrations/type-mapping.md) for manual conversion. Present the proposed rewrite to the user and obtain confirmation before re-running `dsql_lint(fix=true)`; execute only when the re-lint is clean.
122+
- **Timeout:** Retry once. If the retry also times out, inform the user and obtain confirmation before falling back to splitting the SQL at statement boundaries and linting each in a bounded single-pass loop. If an individual statement still times out, stop and surface to the user — do not recurse further.

0 commit comments

Comments
 (0)