I tried the new feature of DMS Schema Conversion "Automation of AI agents using AWS MCP server"
This page has been translated by machine translation. View original
Introduction
AWS DMS Schema Conversion includes an additional AI-powered conversion feature on top of its rule-based conversion engine. Furthermore, by combining Kiro CLI with AWS MCP Server, DMS operations themselves can be automated using natural language.
In this article, we tested how far a schema containing Oracle-specific syntax could be migrated to PostgreSQL using three stages: "rule-based conversion," "generative AI conversion," and "Kiro code generation follow-up."
| Layer | Role |
|---|---|
| AWS MCP Server + Kiro | DMS API orchestration (creating projects, executing conversions, and generating reports using natural language) |
| DMS Schema Conversion rule-based engine | Standard schema conversion (tables, indexes, PL/SQL syntax conversion) |
| DMS Schema Conversion generative AI | Additional conversion for action items determined as "unconvertible" by the rule-based engine |
| Kiro standalone code generation | Supplementary code generation for action items remaining after the above 3 layers |
Verification Details
Verification Environment
| Resource | Specification |
|---|---|
| Source DB | RDS Oracle SE2 19c (db.m5.large, us-west-2) |
| Target DB (DMS) | Virtual Aurora PostgreSQL 15 |
| Target DB (syntax validation) | PostgreSQL 16 on EC2 (Amazon Linux 2023) |
| DMS conversion settings | MaterializedViewConvert=TABLE, target engine version 15 |
| Kiro CLI | Operates DMS API via AWS MCP Server (OAuth) |
Since Oracle SE2 does not support Partitioning (PARTITION BY) and VPD (DBMS_RLS), these are excluded from the verification scope. COMPOUND TRIGGER, DBMS_SCHEDULER, SDO_GEOMETRY, materialized views, and other features available in SE2 were included in the verification scope.
Oracle Schema Configuration
A MIGRATION_TEST schema was created for testing, with the following objects loaded.
| Object | Type | Oracle-specific elements |
|---|---|---|
| audit_log | TABLE | — |
| orders | TABLE | — |
| products | TABLE | SDO_GEOMETRY, XMLTYPE, CLOB, BLOB, TIMESTAMP WITH LOCAL TIME ZONE |
| mv_monthly_sales | MATERIALIZED VIEW | FAST REFRESH ON COMMIT |
| pkg_order_mgmt | PACKAGE | DBMS_SCHEDULER, DBMS_LOB, UTL_FILE, CONNECT BY LEVEL, SYS_REFCURSOR |
| pkg_notification | PACKAGE | — |
| trg_orders_audit | TRIGGER | COMPOUND TRIGGER (AFTER EACH ROW + AFTER STATEMENT) |
Conversion was executed after confirming that all 25 objects (5 tables, 2 packages + 2 bodies, 1 MV, 1 trigger, 6 indexes, 5 LOBs, 3 sequences) were VALID.
Conversion Results by Object
Rule-based conversion and generative AI conversion were each executed in DMS Schema Conversion. The following shows the results of comparing SQL exported to S3 by switching EnableGenAiConversion between false/true.
- Direct DMS application: 8/13 succeeded, 5/13 failed (COMPOUND TRIGGER excluded as no DMS output, including 1 cascading failure)
- Kiro follow-up application: 14/14 syntax application succeeded (1 iteration)
| Object | DMS conversion result | GenAI difference | Direct PG application | Kiro follow-up |
|---|---|---|---|---|
| audit_log (TABLE) | ✅ Success | None | ✅ Success | — |
| orders (TABLE) | ✅ Success | None | ✅ Success | — |
| products (TABLE) | ✅ Success | None | ❌ geometry type missing |
Resolved with PostGIS |
| mv_monthly_sales (TABLE) | ✅ Success | None | ✅ Success | — |
| idx_orders_date (INDEX) | ✅ Success | None | ✅ Success | — |
| i_snap$_mv_monthly_sales (INDEX) | ✅ Success | None | ❌ sys_op_map_nonnull |
Changed to business INDEX |
| PK constraints ×3 | ✅ Success | None | ✅ Success (2/3) | — |
| pkg_notification$send (PROC) | ✅ Success | None | ✅ Success | — |
| pkg_order_mgmt$place_order (PROC) | ⚠️ Partial conversion | Present | ✅ Success | Reimplemented with pg_cron |
| pkg_order_mgmt$archive_old_orders (PROC) | ✅ Success | None | ❌ aws_oracle_ext missing |
Table output + COPY |
| pkg_order_mgmt$get_order_summary (FUNC) | ✅ Success | None | ❌ aws_oracle_data missing |
Reimplemented with generate_series |
| trg_orders_audit (TRIGGER) | ❌ COMPOUND TRIGGER not supported | — | — | Converted to row trigger |
※ The 13 objects for direct DMS application consist of 4 tables (audit_log, orders, products, mv_monthly_sales) + 2 INDEXes + 3 PK constraints + 4 PROC/FUNCTIONs (logical object units from DMS output). COMPOUND TRIGGER is excluded from direct application due to non-support. The 14 objects for Kiro follow-up include the DMS output combined with objects added/modified by Kiro (such as the archive_export table and trigger functions) as application units.
Areas Where Generative AI Conversion Produced Differences (for This Verification Schema)
The only difference between rule-based and generative AI output was in the DBMS_SCHEDULER.CREATE_JOB call within pkg_order_mgmt$place_order.
Rule-based conversion output:
/*
[5501 - Severity CRITICAL - PostgreSQL doesn't support functionality similar to the
SYS.DBMS_SCHEDULER.CREATE_JOB(...) module.
Revise your converted code to use AWS Lambda with scheduled events.]
DBMS_SCHEDULER.CREATE_JOB(
job_name => v_job_name,
job_type => 'PLSQL_BLOCK',
job_action => 'BEGIN migration_test.pkg_notification.send(' || p_order_id || '); END;',
start_date => SYSTIMESTAMP + INTERVAL '5' MINUTE,
enabled => TRUE
)
*/
The rule-based engine comments out the relevant code and proposes it as action item 5501 (CRITICAL), suggesting to "rewrite using AWS Lambda with scheduled events."
Generative AI conversion output:
/* [5444 - Severity LOW - This conversion uses machine learning models that generate
predictions based on patterns in data. Machine learning output is probabilistic and
should be reviewed for accuracy, including human evaluation, as needed for your use case.] */
/* vvv ---- Beginning of statement generated using GenAI. ---- vvv */
CALL DBMS_SCHEDULER.CREATE_JOB(
job_name => v_job_name,
job_type => 'PLSQL_BLOCK',
job_action => 'BEGIN migration_test.pkg_notification.send(' || p_order_id || '); END;',
start_date => aws_oracle_ext.systimestamp() + '5 MINUTE'::INTERVAL,
enabled => TRUE
);
/* ^^^ ---- End of statement generated using GenAI. ---- ^^^ */
The generative AI generates code with syntax conversion applied without commenting out. However, since the DBMS_SCHEDULER package does not exist in PostgreSQL, this code cannot be executed as-is. The original 5501 (CRITICAL) was replaced by 5444 (LOW), which is an action item indicating that this is generative AI output and requires human review.
| Comparison aspect | Rule-based | Generative AI |
|---|---|---|
| Output format | Commented out (not executable) | CALL statement (syntax converted, unresolved dependencies) |
| Severity | CRITICAL (5501) | LOW (5444) |
| SYSTIMESTAMP | Remains as Oracle syntax | aws_oracle_ext.systimestamp() |
| INTERVAL expression | INTERVAL '5' MINUTE |
'5 MINUTE'::INTERVAL |
| Practicality | Manual rewrite required | Does not work as-is |
Within the scope of this verification, generative AI differences were limited to the DBMS_SCHEDULER.CREATE_JOB part that was determined unconvertible by rule-based conversion, and no output differences were confirmed for other objects.
Assessment Report Action Items
The following action items remained in the assessment report when generative AI conversion was enabled.
| Action item | Severity | Content | Resolution |
|---|---|---|---|
| 5242 | — | COMPOUND TRIGGER not supported | Split into row triggers |
| 5444 | LOW | Generative AI conversion result (requires review) | Human review |
| 5584 | — | Timezone configuration confirmation | Manual confirmation |
| 5798 | — | DBMS_LOB temporary LOB behavior difference | Logic confirmation |
| 9994 | — | Unconvertible (UTL_FILE) | Manual conversion |
Applying DMS Conversion Results Directly to PostgreSQL
When the SQL output by DMS rule-based conversion was directly executed on PostgreSQL 16 on EC2, 8 out of 13 succeeded. DMS Schema Conversion was run with target version Aurora PostgreSQL 15, and the following are the syntax validation results on PostgreSQL 16.
Success (8 cases): Table creation for audit_log, orders, mv_monthly_sales; idx_orders_date; 2 PK constraints; pkg_notification$send; place_order (no syntax error since DBMS_SCHEDULER portion is commented out; full functionality reproduction addressed in Kiro follow-up below)
Failure (5 cases):
| Object | Error |
|---|---|
| products (TABLE) | type "geometry" does not exist — PostGIS not installed |
| i_snap$_mv_monthly_sales (INDEX) | column "sys_op_map_nonnull" does not exist — Oracle internal function |
| PK: products | Cascading failure due to products table not being created |
| get_order_summary (FUNCTION) | schema "aws_oracle_data" does not exist — aws_oracle_data schema dependency |
| archive_old_orders (PROCEDURE) | schema "aws_oracle_ext" does not exist — aws_oracle_ext schema dependency |
Some of the DMS conversion results in this verification contained SQL referencing the aws_oracle_ext and aws_oracle_data schemas. Since these schemas do not exist in plain PostgreSQL, DBMS_LOB and UTL_FILE wrapper calls result in errors.
Kiro Follow-up for Unconvertible Parts
We requested Kiro to generate code for the parts that could not be converted by DMS, as well as parts that depended on aws_oracle_ext / aws_oracle_data schemas and would not work on plain PostgreSQL.
DBMS_SCHEDULER → pg_cron (self-deletion pattern)
Oracle's DBMS_SCHEDULER.CREATE_JOB creates a one-time delayed job (to execute 5 minutes later). Since pg_cron is fundamentally based on cron expression periodic execution, it was reproduced using the pattern of "check every minute → process on the first execution opportunity after the specified time → self-deletion." This means that unlike Oracle's strict 5-minute delay, a delay of up to approximately 1 minute may occur.
CREATE OR REPLACE PROCEDURE migration_test.pkg_order_mgmt$place_order(
IN p_customer_id DOUBLE PRECISION,
IN p_total DOUBLE PRECISION,
OUT p_order_id DOUBLE PRECISION
)
LANGUAGE plpgsql
AS $$
DECLARE
v_job_name TEXT;
v_fire_at TIMESTAMP;
BEGIN
INSERT INTO migration_test.orders (customer_id, order_date, total_amount)
VALUES (p_customer_id, CURRENT_TIMESTAMP::TIMESTAMP(0), p_total)
RETURNING order_id INTO p_order_id;
v_job_name := 'notify_' || p_order_id::BIGINT;
v_fire_at := CURRENT_TIMESTAMP + INTERVAL '5 minutes';
PERFORM cron.schedule(
v_job_name,
'* * * * *',
format(
'DO $j$ BEGIN IF CURRENT_TIMESTAMP >= %L::TIMESTAMP THEN '
|| 'CALL migration_test.pkg_notification$send(%s); '
|| 'PERFORM cron.unschedule(%L); END IF; END $j$;',
v_fire_at,
p_order_id,
v_job_name
)
);
END;
$$;
DBMS_LOB + UTL_FILE → Table output + COPY
Oracle's CSV string assembly using DBMS_LOB.CREATETEMPORARY / WRITEAPPEND and file output using UTL_FILE were replaced in PostgreSQL with writing to a table using INSERT ... SELECT and externalizing to CSV using COPY TO. Since Aurora PostgreSQL cannot write directly to the host filesystem, this serves as a common implementation for Aurora environments.
-- Archive output destination table as UTL_FILE alternative
CREATE TABLE migration_test.archive_export(
order_id BIGINT,
total_amount DOUBLE PRECISION,
archived_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE OR REPLACE PROCEDURE migration_test.pkg_order_mgmt$archive_old_orders(
IN p_before_date TIMESTAMP WITHOUT TIME ZONE
)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO migration_test.archive_export (order_id, total_amount)
SELECT order_id, total_amount
FROM migration_test.orders
WHERE order_date < p_before_date;
DELETE FROM migration_test.orders
WHERE order_date < p_before_date;
END;
$$;
CONNECT BY LEVEL → generate_series
DMS converted CONNECT BY LEVEL to WITH RECURSIVE, but in a form that depends on aws_oracle_ext.ADD_MONTHS and aws_oracle_data.TCursorAttributes. Kiro reimplemented it using PostgreSQL-native generate_series + INTERVAL arithmetic.
CREATE OR REPLACE FUNCTION migration_test.pkg_order_mgmt$get_order_summary(
IN p_customer_id DOUBLE PRECISION
)
RETURNS REFCURSOR
LANGUAGE plpgsql
AS $$
DECLARE
v_cursor REFCURSOR;
BEGIN
OPEN v_cursor FOR
SELECT
gs AS month_offset,
date_trunc('month', CURRENT_DATE) - (gs - 1) * INTERVAL '1 month' AS target_month
FROM generate_series(1, 12) AS gs;
RETURN v_cursor;
END;
$$;
This code contains only the skeleton of monthly series generation using generate_series, and does not include customer-specific aggregation logic using p_customer_id (the purpose is to confirm syntax conversion).
COMPOUND TRIGGER → Row trigger
Oracle's COMPOUND TRIGGER accumulates changed rows in an array with AFTER EACH ROW and performs a bulk INSERT with AFTER STATEMENT. Since DMS Schema Conversion does not support COMPOUND TRIGGER (action item 5242), it was left unconverted and reimplemented as a PostgreSQL row trigger.
CREATE OR REPLACE FUNCTION migration_test.trg_orders_audit_fn()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
IF TG_OP = 'DELETE' THEN
INSERT INTO migration_test.audit_log (operation, order_id, event_ts)
VALUES ('DELETE', OLD.order_id, CURRENT_TIMESTAMP);
RETURN OLD;
ELSE
INSERT INTO migration_test.audit_log (operation, order_id, event_ts)
VALUES (TG_OP, NEW.order_id, CURRENT_TIMESTAMP);
RETURN NEW;
END IF;
END;
$$;
CREATE TRIGGER trg_orders_audit
AFTER INSERT OR UPDATE OR DELETE ON migration_test.orders
FOR EACH ROW
EXECUTE FUNCTION migration_test.trg_orders_audit_fn();
Kiro Follow-up Application Results
All 14 objects including the above code were applied to PostgreSQL 16 on EC2, with 14/14 syntax applications succeeding (1 iteration). "Success" here means that CREATE passed on PostgreSQL 16, and does not guarantee functional equivalence with the original Oracle code. As functional testing, INSERT into the orders table → trigger firing → audit_log recording was confirmed. Actual firing by pg_cron after 5 minutes was not confirmed in this verification.
Operations with Kiro + AWS MCP Server
AWS MCP Server was connected to Kiro CLI, and DMS Schema Conversion operations were executed using natural language. Below are representative operation flows.
Example input prompt:
Convert the MIGRATION_TEST schema to Aurora PostgreSQL using rule-based conversion only (GenAI disabled). The migration project is oracle-to-pg-schema-conversion in us-west-2. Export the converted SQL scripts to S3.
In response to this prompt, Kiro called the following APIs in sequence.
describe-conversion-configuration— Check current settingsmodify-conversion-configuration— Change toEnableGenAiConversion=falsestart-metadata-model-conversion— Execute conversionwait metadata-model-converted— Wait for completionstart-metadata-model-export-as-script— S3 exportwait metadata-model-exported-as-script— Wait for export completiondescribe-metadata-model-exports-as-script— Confirm S3 key
A total of 19 API calls were executed across 4 prompts (schema confirmation → rule-based conversion → generative AI conversion → assessment generation).
During the process, an InvalidParameterValueException occurred due to using an incorrect parameter (schema-conversion-operation-id) as the waiter's filter name. Kiro inferred the correct filter name (request-id) from the error message and self-corrected. The same type of error did not recur afterward.
MCP is an automation layer for API calls, and the conversion engine itself is the same. Within the scope of this verification, no differences in conversion results were confirmed between MCP-based and manual operations when executed with the same conversion settings and input schema.
Summary
In this verification, we used DMS Schema Conversion's rule-based conversion and generative AI conversion to convert a schema containing Oracle-specific syntax to PostgreSQL. The main remaining challenges after DMS + generative AI conversion fell into: those requiring additional extensions (PostGIS, pg_cron), those requiring design changes (UTL_FILE → table output + COPY), and those where performance characteristics cannot be simply reproduced (COMPOUND TRIGGER bulk processing).
In the Kiro follow-up, all 14 application units—combining DMS conversion results with code generated and corrected by Kiro—passed PostgreSQL 16 syntax validation in 1 iteration. However, success here refers to CREATE statements passing, and does not guarantee functional equivalence or performance characteristics with the original Oracle code. Actual design decisions and functional verification remain as areas for human responsibility.
In this verification, generative AI conversion differences were confirmed in only one location: DBMS_SCHEDULER.CREATE_JOB. This is partly because the verification schema contained many elements excluded from generative AI conversion targets, such as COMPOUND TRIGGER and INDEXes. For schemas with more applicable elements, there may be more locations where generative AI intervenes.
Where generative AI intervened, rather than leaving code commented out as in rule-based conversion, code attempting PostgreSQL syntax conversion was output. On the other hand, the output CALL DBMS_SCHEDULER.CREATE_JOB(...) is not something that works directly in PostgreSQL, and as indicated by action item 5444, human review and redesign are required.
Orchestration via Kiro + AWS MCP Server does not change the conversion engine itself, as long as DMS Schema Conversion is executed with the same settings. In this verification, 19 API calls were completed with 4 natural language prompts, and intermediate API parameter errors were also self-corrected, confirming its effectiveness in terms of DMS operation efficiency.
Reference Links
