<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Svetozar Radojcin</title>
    <description>The latest articles on DEV Community by Svetozar Radojcin (@java_freepascal_dev).</description>
    <link>https://dev.to/java_freepascal_dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3939868%2F5c813347-997d-4067-926d-2f65ec00c552.png</url>
      <title>DEV Community: Svetozar Radojcin</title>
      <link>https://dev.to/java_freepascal_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/java_freepascal_dev"/>
    <language>en</language>
    <item>
      <title>Teaching an AI to read a relational database — including Oracle APEX</title>
      <dc:creator>Svetozar Radojcin</dc:creator>
      <pubDate>Wed, 22 Jul 2026 07:49:26 +0000</pubDate>
      <link>https://dev.to/java_freepascal_dev/teaching-an-ai-to-read-a-relational-database-including-oracle-apex-4c5p</link>
      <guid>https://dev.to/java_freepascal_dev/teaching-an-ai-to-read-a-relational-database-including-oracle-apex-4c5p</guid>
      <description>&lt;p&gt;A few months back I started building a  tool to solve a problem that kept&lt;br&gt;
annoying me: when you want an AI assistant to help with an old database, you end  up as a human clipboard — copying schema, pasting stored procedures, describing  foreign keys by hand. The AI never actually &lt;em&gt;sees&lt;/em&gt; the database.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Legacy SQL Architect MCP&lt;/strong&gt; — an &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP&lt;/a&gt;  server that connects Claude (or any MCP-compatible client) directly to your  PostgreSQL, SQL Server, or Oracle database, in strict &lt;strong&gt;read-only&lt;/strong&gt; mode. The AI  inspects the real thing instead of guessing from what you paste.&lt;/p&gt;

&lt;p&gt;## The core idea&lt;/p&gt;

&lt;p&gt;Once connected, the assistant can use a set of tools to explore the database on  its own:&lt;/p&gt;

&lt;p&gt;| Tool | What it does |&lt;br&gt;
  |------|--------------|&lt;br&gt;
  | &lt;code&gt;inspect_schema&lt;/code&gt; | Tables, columns, types, primary &amp;amp; foreign keys |&lt;br&gt;
  | &lt;code&gt;data_sampler&lt;/code&gt; | Sample rows, with automatic masking of sensitive columns |&lt;br&gt;
  | &lt;code&gt;get_procedure_source&lt;/code&gt; | Full source of stored procedures and functions |&lt;br&gt;
  | &lt;code&gt;query_plan_expert&lt;/code&gt; | Execution plan analysis, full-scan detection |&lt;br&gt;
  | &lt;code&gt;dependency_graph&lt;/code&gt; | FK chains, trigger → procedure call chains, view deps |&lt;br&gt;
  | &lt;code&gt;generate_mermaid_erd&lt;/code&gt; | An ER diagram in Mermaid |&lt;br&gt;
  | &lt;code&gt;generate_documentation&lt;/code&gt; | Complete Markdown docs for the whole schema |&lt;br&gt;
  | &lt;code&gt;find_impact&lt;/code&gt; | Everything that depends on a given table |&lt;br&gt;
  | &lt;code&gt;generate_java_dao&lt;/code&gt; | Ready-to-compile Java Entity + Repository classes |&lt;/p&gt;

&lt;p&gt;So you can say things like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Inspect the schema, sample the orders table, read the procedures that touch  it, and tell me what would break if I rename &lt;code&gt;order_status&lt;/code&gt;."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;…and the assistant actually goes and looks, then answers with references to real   objects. Read-only by design — it physically cannot INSERT, UPDATE, or DELETE.&lt;/p&gt;

&lt;p&gt;## The new part: Oracle APEX&lt;/p&gt;

&lt;p&gt;This is the release I want to talk about, and I'll be honest about how it came to be. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'm not an APEX developer.&lt;/strong&gt; I've never shipped a production APEX app. But APEX  performance problems are notoriously hard to see, and it turns out Oracle exposes a &lt;em&gt;lot&lt;/em&gt; about an APEX application through its own dictionary views —&lt;br&gt;
  &lt;code&gt;APEX_APPLICATIONS&lt;/code&gt;,&lt;code&gt;APEX_APPLICATION_PAGE_REGIONS&lt;/code&gt;,&lt;code&gt;APEX_APPLICATION_LOVS&lt;/code&gt;, &lt;code&gt;APEX_WORKSPACE_ACTIVITY_LOG&lt;/code&gt;, &lt;code&gt;APEX_DEBUG_MESSAGES&lt;/code&gt;, and friends. The answer to "why is this page slow?" is often sitting right there.&lt;/p&gt;

&lt;p&gt;So I studied those views and turned them into six tools that hand that insight to  the AI in a form it can reason about. Every one was verified against a &lt;strong&gt;live  APEX 26.1 instance&lt;/strong&gt; before shipping.&lt;/p&gt;

&lt;p&gt;### &lt;code&gt;inspect_apex_performance&lt;/code&gt;&lt;br&gt;
  An overview of an application: pages, regions with SQL, LOVs, validations,  processes — plus the &lt;strong&gt;slowest pages&lt;/strong&gt; by real user activity, and a list of  recommendations (slow pages, &lt;code&gt;SELECT *&lt;/code&gt; regions, LOVs with no &lt;code&gt;WHERE&lt;/code&gt; clause,   unconditional processes, …).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Run inspect_apex_performance on app 102 and show me the 5 slowest pages and why."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;### &lt;code&gt;get_apex_source&lt;/code&gt;&lt;br&gt;
  The APEX counterpart to &lt;code&gt;get_procedure_source&lt;/code&gt;: it extracts the embedded code   across 12 component types (regions, processes, computations, validations, items,  branches, dynamic actions, LOVs, authorizations, …). It introspects each view's  columns first, so it tolerates dictionary drift between APEX versions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Get the source of every SQL region on page 3 and summarize what they query."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;### &lt;code&gt;inspect_apex_debug&lt;/code&gt;&lt;br&gt;
  Reads &lt;code&gt;APEX_DEBUG_MESSAGES&lt;/code&gt;, reconstructs page views by grouping on the page-view  id, finds the slowest ones, and drills into the individual steps — so you can see  &lt;em&gt;where inside a page render&lt;/em&gt; the time actually went.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Which page views in the last 2 days were slowest, and what step dominated?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;### &lt;code&gt;apex_config_audit&lt;/code&gt;&lt;br&gt;
  A static anti-pattern audit — no runtime data needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pagination schemes that force a &lt;code&gt;COUNT(*)&lt;/code&gt; ("row N of Z")&lt;/li&gt;
&lt;li&gt;reports with unbounded / very high max-rows&lt;/li&gt;
&lt;li&gt;frequently-used LOVs with no caching&lt;/li&gt;
&lt;li&gt;pages with too many server-side dynamic actions&lt;/li&gt;
&lt;li&gt;pages crowded with many SQL regions&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Audit app 102 for configuration anti-patterns."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;### &lt;code&gt;apex_sql_runtime_stats&lt;/code&gt;&lt;br&gt;
  Correlates the app to its real runtime cost by reading &lt;code&gt;V$SQL&lt;/code&gt; for the app's  parsing schema — buffer gets, elapsed time, executions — and flags the hot SQL.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"What's the most expensive SQL this app has actually run? Order by buffer gets  per execution."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;### &lt;code&gt;apex_explain_batch&lt;/code&gt;&lt;br&gt;
  Runs &lt;code&gt;EXPLAIN PLAN&lt;/code&gt; across an app's SQL regions, SQL LOVs, and Exists-type validations at once, then flags the red lines: &lt;code&gt;TABLE ACCESS FULL&lt;/code&gt;,&lt;br&gt;
  &lt;code&gt;MERGE JOIN CARTESIAN&lt;/code&gt;, &lt;code&gt;INDEX FULL SCAN&lt;/code&gt;. (It handles APEX &lt;code&gt;&amp;amp;SUBSTITUTION.&lt;/code&gt;  tokens by binding them, so the plans actually parse.)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Explain-plan all the SQL in app 102 and list anything doing a full scan or a  Cartesian join."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;## None of this is magic&lt;/p&gt;

&lt;p&gt;It's all sitting in Oracle's own dictionary. What the tool does is connect the  dots and hand them to the AI in a form it can reason about — grounded in your  actual application, not a generic "best practices" lecture.&lt;/p&gt;

&lt;p&gt;## An honest ask&lt;/p&gt;

&lt;p&gt;Because I'm &lt;em&gt;not&lt;/em&gt; an APEX expert, I'd genuinely value review from people who are.  Am I reading the right views? Is there a metric you always check first that I'm   ignoring? Did I get something wrong? You know this platform far better than I do.&lt;/p&gt;

&lt;p&gt;It's open source and free (Apache 2.0), with native installers for Windows and   Linux (deb + rpm) that bundle their own Java runtime — no JDK needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/tabforgeai/legacy-sql-architect-mcp" rel="noopener noreferrer"&gt;https://github.com/tabforgeai/legacy-sql-architect-mcp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it against a real APEX app, I'd love to hear what it found — or where it fell short.&lt;/p&gt;

</description>
      <category>oracle</category>
      <category>apex</category>
      <category>ai</category>
      <category>database</category>
    </item>
    <item>
      <title>We built a bidirectional context loop between web apps and AI agents</title>
      <dc:creator>Svetozar Radojcin</dc:creator>
      <pubDate>Thu, 25 Jun 2026 13:39:49 +0000</pubDate>
      <link>https://dev.to/java_freepascal_dev/we-built-a-bidirectional-context-loop-between-web-apps-and-ai-agents-2mfg</link>
      <guid>https://dev.to/java_freepascal_dev/we-built-a-bidirectional-context-loop-between-web-apps-and-ai-agents-2mfg</guid>
      <description>&lt;p&gt;Source code: &lt;a href="https://github.com/tabforgeai/tabforge-ai" rel="noopener noreferrer"&gt;Tabforge AI&lt;/a&gt;&lt;br&gt;
Most AI integrations in business apps feel the same:&lt;/p&gt;

&lt;p&gt;You send a prompt → the model returns an answer → you try to glue it into your app.&lt;/p&gt;

&lt;p&gt;It works, but it always feels slightly disconnected from what’s actually happening in the UI.&lt;/p&gt;

&lt;p&gt;The AI doesn’t really know what the user is doing.&lt;/p&gt;

&lt;p&gt;And the app doesn’t really know what the AI just did.&lt;/p&gt;

&lt;p&gt;We ran into that problem while building EasyAI / TabForge, and ended up with something we didn’t originally set out to build:&lt;/p&gt;

&lt;p&gt;a bidirectional context loop between the application and the AI runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; AI in apps is stateless&lt;/p&gt;

&lt;p&gt;Even when you pass “context”, it’s usually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a string&lt;/li&gt;
&lt;li&gt;a JSON blob&lt;/li&gt;
&lt;li&gt;or a manually assembled prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the real state lives elsewhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which record is open&lt;/li&gt;
&lt;li&gt;what tab the user is on&lt;/li&gt;
&lt;li&gt;what action just happened&lt;/li&gt;
&lt;li&gt;what step in a workflow the user is in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you end up doing things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;passing IDs around&lt;/li&gt;
&lt;li&gt;re-sending state on every request&lt;/li&gt;
&lt;li&gt;rebuilding context for every AI call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It works, but it’s fragile.&lt;br&gt;
**&lt;br&gt;
Ambient Activity Memory (App → AI)**&lt;/p&gt;

&lt;p&gt;The first thing we added was a way for the application to continuously describe what is happening inside it.&lt;/p&gt;

&lt;p&gt;Not as logs.Not as analytics.&lt;br&gt;
But as structured semantic events tied to actual UI actions.&lt;/p&gt;

&lt;p&gt;So instead of:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“here is an order id”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;the system already knows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;user is currently viewing Order #248&lt;/li&gt;
&lt;li&gt;user just switched from payment tab to details tab&lt;/li&gt;
&lt;li&gt;user just triggered refund flow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now when the user says:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“cancel this order”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;there is no ambiguity about what “this” refers to. The AI doesn’t guess context.It already has it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EasyAIEvent (AI → App)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the AI started understanding the app state, the next obvious question was:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;what does the AI give back to the application?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Not just a final answer, but the execution itself.&lt;/p&gt;

&lt;p&gt;So every agent run can optionally emit a structured event stream:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;started&lt;/li&gt;
&lt;li&gt;planning&lt;/li&gt;
&lt;li&gt;tool calls&lt;/li&gt;
&lt;li&gt;progress updates&lt;/li&gt;
&lt;li&gt;completion
This is exposed via a simple hook:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;.withEventListener(event -&amp;gt; {&lt;br&gt;
    log.info("[{}] {} — {}", event.source(), event.phase(), event.title());&lt;br&gt;
})&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;No framework coupling.No HTTP assumptions.No UI dependencies.&lt;br&gt;
Just a pure event stream that your app can consume however it wants.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What this enables (more interesting part)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once you have both directions:&lt;br&gt;
  App → AI&lt;/p&gt;

&lt;p&gt;The system knows what the user is doing.&lt;br&gt;
   AI → App&lt;/p&gt;

&lt;p&gt;The system exposes what the agent is doing. You end up with something simple but powerful:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;a closed loop between UI state and AI execution state&lt;/em&gt;&lt;br&gt;
**&lt;br&gt;
Why this matters in practice**&lt;/p&gt;

&lt;p&gt;This removes a bunch of glue code that usually creeps into AI integrations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;passing IDs back and forth&lt;/li&gt;
&lt;li&gt;manually constructing prompts&lt;/li&gt;
&lt;li&gt;debugging “what did the agent actually do?”&lt;/li&gt;
&lt;li&gt;rebuilding context on every request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the app continuously emits context&lt;/li&gt;
&lt;li&gt;the AI continuously emits execution state&lt;/li&gt;
&lt;li&gt;both stay decoupled, but synchronized&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important design choice&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The event model is intentionally framework-agnostic:&lt;br&gt;
(source, phase, status, title, detail, toolName, sequence, timestamp)&lt;/p&gt;

&lt;p&gt;It does not know anything about:&lt;/p&gt;

&lt;p&gt;HTTP, WebSockets, SSE, UI frameworks&lt;/p&gt;

&lt;p&gt;That part is left to the application.&lt;/p&gt;

&lt;p&gt;We ship a minimal example that maps the event stream to a real-time UI panel using SSE, but it stays outside the core library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where this is going&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The interesting part is not the event system itself. It’s what becomes possible when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the app knows what the user is doing&lt;/li&gt;
&lt;li&gt;the AI knows what it is doing&lt;/li&gt;
&lt;li&gt;and both sides share the same runtime context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You start to move from &lt;em&gt;“AI calls inside an app”&lt;/em&gt; toward something closer to:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI as a participant in application execution, not just a function you call&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you strip everything away&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At its core, this is all we tried to solve:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How do we make AI systems aware of application state without coupling them to the UI?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And the answer turned out to be:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t pass state. Stream it in both directions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to explore it:&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=Nm5RPV49Qck" rel="noopener noreferrer"&gt;The AI assistant knows what user doing in the app&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=qNDD9mfFEJk" rel="noopener noreferrer"&gt;AI streams his work details to the app&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/tabforgeai/tabforge-ai" rel="noopener noreferrer"&gt;https://github.com/tabforgeai/tabforge-ai&lt;/a&gt;&lt;br&gt;
Full example: &lt;a href="https://github.com/tabforgeai/tabforge-ai-demo" rel="noopener noreferrer"&gt;https://github.com/tabforgeai/tabforge-ai-demo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
