<?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: Oracle Developers</title>
    <description>The latest articles on DEV Community by Oracle Developers (oracledevs).</description>
    <link>https://dev.to/oracledevs</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%2Forganization%2Fprofile_image%2F11587%2F7c934ee0-6aa6-42f9-b43f-91e6fa82ef41.png</url>
      <title>DEV Community: Oracle Developers</title>
      <link>https://dev.to/oracledevs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/oracledevs"/>
    <language>en</language>
    <item>
      <title>Develop A2UI and MCP Apps with Oracle AI Database and the Java MCP Toolkit running in Google Gemini Enterprise App, ChatGPT, and Claude</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Wed, 09 Sep 2026 14:48:33 +0000</pubDate>
      <link>https://dev.to/oracledevs/develop-a2ui-and-mcp-apps-with-oracle-ai-database-and-the-java-mcp-toolkit-running-in-google-gemini-48m6</link>
      <guid>https://dev.to/oracledevs/develop-a2ui-and-mcp-apps-with-oracle-ai-database-and-the-java-mcp-toolkit-running-in-google-gemini-48m6</guid>
      <description>&lt;p&gt;Build dynamic agent experiences with trusted native A2UI components or portable, sandboxed MCP Apps while keeping rendering, interaction, and execution boundaries explicit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit" rel="noopener noreferrer"&gt;Source code for this supply-chain reference application&lt;/a&gt;&lt;/p&gt;





&lt;h2&gt;Key Takeaways&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A planner can review a database-calculated recommendation while Oracle AI Database retains final transaction and audit authority.&lt;/li&gt;



&lt;li&gt;Oracle AI Database provides governed data services that the Oracle Database MCP Java Toolkit exposes as reusable, bounded operations, helping AI agents query business data safely through MCP.&lt;/li&gt;



&lt;li&gt;A2UI is a runtime protocol, not a code generator: an agent describes intent as validated JSON and the host renders approved native components from its own design system.&lt;/li&gt;



&lt;li&gt;Rich agent tasks need interfaces beyond chat. A2UI provides safe, consistent composition; MCP Apps provide portable custom web experiences through sandboxed&amp;nbsp;&lt;code&gt;ui://&lt;/code&gt;&amp;nbsp;resources.&lt;/li&gt;



&lt;li&gt;Both UI paths can collect explicit human approval while the application and database retain execution authority.&lt;/li&gt;
&lt;/ul&gt;





&lt;p&gt;How can an AI agent build a useful interface during a task while keeping data access and execution governed? This runnable supply-chain reference separates each responsibility:&lt;/p&gt;

&lt;p&gt;A planner reviews a database-calculated recommendation in a task-specific interface, while Oracle AI Database retains final authority over validation, locking, the transaction, and its audit record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP Apps and A2UI address the frontend version of the vibe-coding risk seen in tools such as Lovable and v0, where arbitrary generated UI code needs boundaries, just as the Oracle Database MCP Java Toolkit addresses backend MCP risk by exposing allowlisted, governed database tools instead of arbitrary server capabilities.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Together, MCP Toolkit for backend tools, A2A for agent-to-agent or host-to-agent communication, and A2UI or MCP Apps for frontend interaction form a practical full-stack pattern for safer agentic AI applications.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Oracle AI Database&lt;/strong&gt;&amp;nbsp;calculates feasible source-to-target recommendations and remains the authoritative data, transaction, and audit layer.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Oracle Database MCP Java Toolkit&lt;/strong&gt;&amp;nbsp;exposes reusable, allowlisted business tools instead of unrestricted SQL or database write access.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;A2UI&lt;/strong&gt;&amp;nbsp;lets an agent dynamically build and update an interface by sending a structured JSON recipe that the client securely renders with pre-approved native components.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;MCP Apps&lt;/strong&gt;&amp;nbsp;package a developer-built HTML and JavaScript application as a sandboxed&amp;nbsp;&lt;code&gt;ui://&lt;/code&gt;&amp;nbsp;resource for a compatible conversational application.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;AG-UI&lt;/strong&gt;, when a custom frontend needs it, streams agent events, state, text, and tool lifecycles between the service and that frontend.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;A2A&lt;/strong&gt;&amp;nbsp;connects a conversational application such as Gemini Enterprise to the independently deployed A2UI-producing agent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The MCP Toolkit approach is well suited to this architecture because the same governed tool contracts can be reused by multiple agents, custom frontends, and MCP-compatible clients.&lt;/p&gt;

&lt;p&gt;All Java, JavaScript, TypeScript, SQL, YAML, security notes, and setup instructions are in the&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit" rel="noopener noreferrer"&gt;project folder on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Video walkthrough:&lt;/strong&gt;&amp;nbsp;Watch the complete&amp;nbsp;&lt;a href="https://youtu.be/FZGAqpYul1A" rel="noopener noreferrer"&gt;A2UI, MCP Apps, Oracle Database MCP Java Toolkit, and Oracle AI Database demonstration on YouTube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code and config deep dive:&lt;/strong&gt;&amp;nbsp;Watch the companion&amp;nbsp;&lt;a href="https://youtu.be/iAASqFO7AKw" rel="noopener noreferrer"&gt;source, configuration, and runtime walkthrough on YouTube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8g1fjnkl0l13epyfjc9m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8g1fjnkl0l13epyfjc9m.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A2UI version compatibility:&lt;/strong&gt;&amp;nbsp;the standalone browser adapter emits A2UI v0.9.1 in AG-UI events, while the checked-in Gemini Enterprise adapter emits A2UI v0.8 DataParts over A2A. These are separate host adapters over the same service, not mixed versions on one UI surface. Each host validates the version and catalog it advertises. Gemini Enterprise currently documents both A2UI v0.8 and v0.9 support.&lt;/p&gt;
&lt;/blockquote&gt;





&lt;h2&gt;Why Agent-Generated UI Needs a Runtime Contract&lt;/h2&gt;

&lt;p&gt;Agents increasingly need forms, comparisons, visualizations, and approval controls, but production applications should not execute arbitrary model-generated frontend code. Fixed widgets are safer yet too rigid for task-specific interfaces.&lt;/p&gt;

&lt;p&gt;A2UI closes that gap with declarative JSON for components, layout, data bindings, and actions. A&amp;nbsp;&lt;strong&gt;host&lt;/strong&gt;&amp;nbsp;is the user-facing application. In this article, the standalone browser and Gemini Enterprise demonstrate host-rendered A2UI, while Gemini Enterprise, ChatGPT, and Claude demonstrate MCP Apps. An A2UI host's&amp;nbsp;&lt;strong&gt;component catalog&lt;/strong&gt;&amp;nbsp;is the allowlist of native controls it knows how to validate and render. The host preserves its design system and accessibility while rejecting unsupported capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The result is a UI that can appear and update during a conversation without executing agent-generated frontend code. The host controls rendering, and Oracle AI Database retains authority over the transaction.&lt;/strong&gt;&lt;/p&gt;





&lt;h2&gt;Which Protocols Connect Which Responsibility?&lt;/h2&gt;

&lt;p&gt;The host examples in this article are the standalone browser, Gemini Enterprise, ChatGPT, and Claude. Each supports a different combination of transport and UI capabilities.&lt;/p&gt;

&lt;p&gt;AG-UI is not required merely because an application uses A2UI or MCP Apps. AG-UI is an optional event transport for a custom frontend; A2UI is a declarative JSON UI specification; and MCP Apps package a developer-built web application. A design can use any one of them or combine them when their separate responsibilities are needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fptws3zhua8yi8gwk9o74.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fptws3zhua8yi8gwk9o74.png" alt=" " width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj1vr1suhoeulwk4za6bm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj1vr1suhoeulwk4za6bm.png" alt=" " width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Primary job&lt;/th&gt;
&lt;th&gt;In this application&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AG-UI&lt;/td&gt;
&lt;td&gt;Optional transport pipeline for tokens, events, tool lifecycles, and shared state between an agent backend and a custom frontend.&lt;/td&gt;
&lt;td&gt;Carries run lifecycle, text, MCP tool activity, A2UI messages, state, and errors over SSE.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A2UI&lt;/td&gt;
&lt;td&gt;Declarative, streaming JSON recipe that lets an agent dynamically build and update a UI from the host's pre-approved native component catalog.&lt;/td&gt;
&lt;td&gt;Defines inventory cards, notes, approval, and cancel controls.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP Apps&lt;/td&gt;
&lt;td&gt;Developer-built HTML and JavaScript application returned as a&amp;nbsp;&lt;code&gt;ui://&lt;/code&gt;&amp;nbsp;resource for a host sandbox.&lt;/td&gt;
&lt;td&gt;Provides the portable recommendation dashboard for compatible conversational hosts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP&lt;/td&gt;
&lt;td&gt;Standardizes discovery and invocation of tools and resources.&lt;/td&gt;
&lt;td&gt;Connects the service to five governed Oracle Database operations.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A2A&lt;/td&gt;
&lt;td&gt;Standardizes agent discovery, requests, and results.&lt;/td&gt;
&lt;td&gt;Carries host requests and A2UI v0.8 DataParts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle Database MCP Java Toolkit&lt;/td&gt;
&lt;td&gt;Expose reusable Oracle Database capabilities.&lt;/td&gt;
&lt;td&gt;Runs the exact YAML-defined allowlist of reads and procedure-backed writes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle AI Database&lt;/td&gt;
&lt;td&gt;Remain the trusted data and execution layer.&lt;/td&gt;
&lt;td&gt;Computes recommendations and enforces locking, transactions, authorization, and auditing.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;GitHub Repos Map&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Directory&lt;/th&gt;
&lt;th&gt;What it contains&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit/agent-service" rel="noopener noreferrer"&gt;&lt;code&gt;agent-service/&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Java orchestration, Streamable HTTP MCP client, approval state, AG-UI events, REST endpoints, and A2UI payloads.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit/oracle-db-mcp-toolkit" rel="noopener noreferrer"&gt;&lt;code&gt;oracle-db-mcp-toolkit/&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Standalone Toolkit startup, TLS setup, YAML tool definitions, contracts, and pinned-runtime support.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit/database" rel="noopener noreferrer"&gt;&lt;code&gt;database/&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Sample schema, inventory data, recommendation view, transfer audit, and approval procedure.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit/web-client" rel="noopener noreferrer"&gt;&lt;code&gt;web-client/&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Standalone browser that consumes AG-UI events and renders allowlisted A2UI components.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit/gemini-enterprise-a2a" rel="noopener noreferrer"&gt;&lt;code&gt;gemini-enterprise-a2a/&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A2A endpoint, generated agent card, Java-service adapter, and A2UI v0.8 response builder.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit/mcp-app" rel="noopener noreferrer"&gt;&lt;code&gt;mcp-app/&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;TypeScript MCP server,&amp;nbsp;&lt;code&gt;ui://&lt;/code&gt;&amp;nbsp;resource, sandboxed dashboard, and host-bridge actions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit/deploy/gcp" rel="noopener noreferrer"&gt;&lt;code&gt;deploy/gcp/&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;macOS/Linux deployment scripts and Cloud Build configuration for hosted adapters and MCP Apps.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit/scripts" rel="noopener noreferrer"&gt;&lt;code&gt;scripts/&lt;/code&gt;&lt;/a&gt;,&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit/docs" rel="noopener noreferrer"&gt;&lt;code&gt;docs/&lt;/code&gt;&lt;/a&gt;, and&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit/images" rel="noopener noreferrer"&gt;&lt;code&gt;images/&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Smoke tests, setup guidance, sequence diagrams, and sanitized product captures.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;





&lt;h2&gt;From Stockout Analysis to an Inventory-Transfer Decision&lt;/h2&gt;

&lt;p&gt;Consider the request: “Show inventory at risk of stockout, explain the demand and safety-stock factors, and let me approve a feasible transfer from another location.” A useful supply-chain AI application must expose more than a prose answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the current run status and governed database tool being invoked;&lt;/li&gt;



&lt;li&gt;a bounded list of products and target locations at risk;&lt;/li&gt;



&lt;li&gt;on-hand, reserved, inbound, forecast, safety-stock, and shortage quantities;&lt;/li&gt;



&lt;li&gt;a feasible source location, transfer quantity, transit time, and cost;&lt;/li&gt;



&lt;li&gt;an explicit approval boundary with an operator note; and&lt;/li&gt;



&lt;li&gt;a transfer ID and auditable completion state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The agent can decide when to request recommendations and how to explain them. It cannot choose an arbitrary transfer quantity, execute generated SQL, create new UI capabilities, or substitute a different source and target after approval. These enforced boundaries provide application and database guardrails that reduce manipulation, unauthorized operations, and unintended writes.&lt;/strong&gt;&lt;/p&gt;





&lt;h2&gt;How the Shared Architecture Connects&lt;/h2&gt;

&lt;p&gt;A2UI does not mandate a transport. The standalone browser example receives A2UI v0.9.1 inside AG-UI&amp;nbsp;&lt;code&gt;CUSTOM&lt;/code&gt;&amp;nbsp;events, while the Gemini Enterprise Apps example receives A2UI v0.8 DataParts over A2A. The MCP Apps use MCP Streamable HTTP and a host bridge instead of either transport used to carry A2UI.&lt;/p&gt;

&lt;p&gt;A&amp;nbsp;&lt;strong&gt;host adapter&lt;/strong&gt;&amp;nbsp;translates the shared domain result into the contract supported by a particular host.&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/gemini-enterprise-a2a/main.py" rel="noopener noreferrer"&gt;&lt;code&gt;gemini-enterprise-a2a/main.py&lt;/code&gt;&lt;/a&gt;&amp;nbsp;adapts the result to A2A/A2UI for Gemini Enterprise;&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/mcp-app/server.ts" rel="noopener noreferrer"&gt;&lt;code&gt;mcp-app/server.ts&lt;/code&gt;&lt;/a&gt;&amp;nbsp;exposes the MCP App used by ChatGPT, Claude, and Gemini Enterprise; and&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/web-client/app.js" rel="noopener noreferrer"&gt;&lt;code&gt;web-client/app.js&lt;/code&gt;&lt;/a&gt;&amp;nbsp;consumes AG-UI/A2UI in the standalone browser. These adapters do not duplicate the Oracle query or approval policy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do MCP Apps use A2UI?&lt;/strong&gt;&amp;nbsp;Not automatically. This reference application implements two alternative UI paths over the same governed service: a host-rendered A2UI path and a sandboxed MCP App path. The official A2UI project also defines optional composition patterns for&amp;nbsp;&lt;a href="https://a2ui.org/guides/mcp-apps-in-a2ui/" rel="noopener noreferrer"&gt;embedding an MCP App as a custom component in an A2UI surface&lt;/a&gt;&amp;nbsp;and for&amp;nbsp;&lt;a href="https://a2ui.org/guides/a2ui-in-mcp-apps/" rel="noopener noreferrer"&gt;bundling an A2UI renderer inside an MCP App&lt;/a&gt;. Those patterns require explicit renderer, catalog, bridge, and sandbox integration; using either protocol alone does not activate the other.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F16ils147uth58om0t9hp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F16ils147uth58om0t9hp.png" alt=" " width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Keep the boundaries clear:&lt;/strong&gt;&amp;nbsp;the Oracle Database MCP Java Toolkit is database-facing. Orchestration and approval policy belong in the agent service; A2UI rendering belongs in the host or web client; rich MCP App content belongs behind the host bridge.&lt;/p&gt;
&lt;/blockquote&gt;





&lt;h2&gt;Expose Bounded Supply-Chain Tools with the Oracle Database MCP Java Toolkit&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Toolkit supports Deep Data Security:&lt;/strong&gt;&amp;nbsp;database-enforced row and column authorization.&lt;/p&gt;

&lt;p&gt;The Toolkit configuration enables exactly five business operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;find-stockout-transfer-recommendations&lt;/code&gt;&amp;nbsp;returns bounded recommendations above a risk threshold.&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;get-stockout-transfer-details&lt;/code&gt;&amp;nbsp;retrieves one current recommendation by its compound ID.&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;reserve-inventory-transfer-id&lt;/code&gt;&amp;nbsp;obtains the next audit identifier.&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;approve-inventory-transfer&lt;/code&gt;&amp;nbsp;calls the locked, procedure-backed write.&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;count-inventory-transfers&lt;/code&gt;&amp;nbsp;verifies the resulting audit record.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Terminology:&lt;/strong&gt;&amp;nbsp;“bounded” or “narrow” is descriptive shorthand here, not a formal MCP or Oracle Toolkit tool category. It means a purpose-built, allowlisted operation with constrained inputs and outputs, bind variables, explicit validation, and least-privileged database access. Those properties make behavior safer, more deterministic, and more auditable than unrestricted SQL; they do not make a tool absolutely safe.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following toolset and bind-variable query are defined in&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/oracle-db-mcp-toolkit/config/tools.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;oracle-db-mcp-toolkit/config/tools.yaml&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;toolsets:
  supply-chain-exchange:
    - find-stockout-transfer-recommendations
    - get-stockout-transfer-details
    - reserve-inventory-transfer-id
    - approve-inventory-transfer
    - count-inventory-transfers

tools:
  find-stockout-transfer-recommendations:
    dataSource: financial-db
    parameters:
      - name: minimumStockoutRisk
        type: number
        required: true
      - name: maximumRows
        type: integer
        required: true
    statement: &amp;gt;-
      SELECT * FROM (
        SELECT *
          FROM stockout_transfer_recommendation_v
         WHERE stockout_risk_score &amp;gt;= :minimumStockoutRisk
         ORDER BY stockout_risk_score DESC
      ) WHERE ROWNUM &amp;lt;= :maximumRows&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;McpToolkitSupplyChainGateway&lt;/code&gt;&amp;nbsp;connects to the independently running Toolkit through authenticated Streamable HTTP, completes MCP initialization, verifies the server identity, and requires the exact&amp;nbsp;&lt;code&gt;supply-chain-exchange&lt;/code&gt;&amp;nbsp;allowlist. Built-in&amp;nbsp;&lt;code&gt;read-query&lt;/code&gt;,&amp;nbsp;&lt;code&gt;write-query&lt;/code&gt;, table, and administration tools are never enabled.&lt;/p&gt;

&lt;p&gt;The following excerpt from&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/agent-service/src/main/java/com/oracle/demo/interactiveai/McpToolkitSupplyChainGateway.java" rel="noopener noreferrer"&gt;&lt;code&gt;agent-service/src/main/java/com/oracle/demo/interactiveai/McpToolkitSupplyChainGateway.java&lt;/code&gt;&lt;/a&gt;&amp;nbsp;validates input and invokes the named MCP tool rather than accepting generated SQL:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;InputValidation.minimumStockoutRisk(minimumStockoutRisk);
InputValidation.maximumRows(maximumRows);

return rows(client.callTool(
    "find-stockout-transfer-recommendations",
    Map.of(
        "minimumStockoutRisk", minimumStockoutRisk,
        "maximumRows", maximumRows)))
    .stream()
    .map(row -&amp;gt; new TransferRecommendation(/* typed columns */))
    .toList();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The same file fails closed if discovery returns anything other than the expected tool names:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Set&amp;lt;String&amp;gt; available = client.listTools();
if (!available.equals(requiredTools)) {
  throw new IllegalStateException(
      "Oracle Database MCP Toolkit tool allowlist mismatch");
}&lt;/code&gt;&lt;/pre&gt;





&lt;h2&gt;Choose A2UI Consistency or MCP App Flexibility&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;UI contract&lt;/th&gt;
&lt;th&gt;How it works&lt;/th&gt;
&lt;th&gt;Security method&lt;/th&gt;
&lt;th&gt;Advantage&lt;/th&gt;
&lt;th&gt;Tradeoff&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A2UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The host renders the agent's validated declarative recipe with trusted native components.&lt;/td&gt;
&lt;td&gt;The host validates the payload and permits only components, properties, and actions in its approved catalog.&lt;/td&gt;
&lt;td&gt;Native appearance, accessibility, predictable behavior, and a host-controlled catalog.&lt;/td&gt;
&lt;td&gt;The agent can compose supported controls but cannot invent a new component or arbitrary JavaScript.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP Apps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An agent or MCP server returns an interactive HTML/JavaScript resource through a&amp;nbsp;&lt;code&gt;ui://&lt;/code&gt;&amp;nbsp;pointer; the host renders it in a sandboxed iframe or mobile WebView.&lt;/td&gt;
&lt;td&gt;The host isolates the app with a sandbox, CSP, permissions policy, and validated bridge; the MCP server still authenticates and authorizes every tool call.&lt;/td&gt;
&lt;td&gt;Custom charts, maps, canvases, and complete mini-applications can travel with the MCP server without being prebuilt into every host frontend.&lt;/td&gt;
&lt;td&gt;The sandbox adds overhead, Content Security Policy constraints, and a visual boundary that may not perfectly match the host's native interface.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The practical choice is host-native catalog consistency or developer-supplied presentation flexibility. Both can be secured, using the different controls summarized above, and both retain the same governed recommendation and approval workflow.&lt;/p&gt;

&lt;p&gt;The Oracle logo illustrates the distinct controls. The MCP App permits the external image through&amp;nbsp;&lt;code&gt;resourceDomains&lt;/code&gt;&amp;nbsp;in&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/mcp-app/server.ts" rel="noopener noreferrer"&gt;&lt;code&gt;mcp-app/server.ts&lt;/code&gt;&lt;/a&gt;. The A2UI path separately requires the&amp;nbsp;&lt;code&gt;Image&lt;/code&gt;&amp;nbsp;component and permits&amp;nbsp;&lt;code&gt;https://www.oracle.com&lt;/code&gt;&amp;nbsp;through&amp;nbsp;&lt;code&gt;allowedImageOrigins&lt;/code&gt;&amp;nbsp;in&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/web-client/app.js" rel="noopener noreferrer"&gt;&lt;code&gt;web-client/app.js&lt;/code&gt;&lt;/a&gt;. An A2UI payload does not inherit an MCP App's CSP.&lt;/p&gt;





&lt;h2&gt;Render Recommendation and Approval Controls with A2UI&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/agent-service/src/main/java/com/oracle/demo/interactiveai/A2uiPayloads.java" rel="noopener noreferrer"&gt;&lt;code&gt;agent-service/src/main/java/com/oracle/demo/interactiveai/A2uiPayloads.java&lt;/code&gt;&lt;/a&gt;&amp;nbsp;creates one A2UI v0.9.1 surface, supplies an adjacency-list component tree, and sends recommendation data separately:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  "version": "v0.9.1",
  "createSurface": {
    "surfaceId": "inventory-transfer-review",
    "catalogId": "https://a2ui.org/specification/v0_9_1/catalogs/basic/catalog.json",
    "sendDataModel": true
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Gemini Enterprise also publishes a&amp;nbsp;&lt;a href="https://www.gstatic.com/vertexaisearch/a2ui/v0_9/gemini_enterprise_composite_catalog.json" rel="noopener noreferrer"&gt;&lt;code&gt;gemini_enterprise_composite_catalog.json&lt;/code&gt;&lt;/a&gt;&amp;nbsp;example that combines standard Material components with Gemini Enterprise components. This application uses the standard catalog IDs advertised in its agent card.&lt;/p&gt;

&lt;p&gt;The same class requests an&amp;nbsp;&lt;code&gt;Image&lt;/code&gt;&amp;nbsp;from the host's approved Basic Catalog and declares the bounded action names:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Map.of(
    "id", "oracleLogo",
    "component", "Image",
    "url", "https://www.oracle.com/a/ocom/img/oracle-logo.svg",
    "description", "Oracle logo for Oracle AI Database",
    "fit", "contain",
    "variant", "header");

Map.of(
    "id", "confirm",
    "component", "Button",
    "text", "Approve inventory transfer",
    "action", Map.of("event", Map.of(
        "name", "approve_inventory_transfer")))&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/web-client/app.js" rel="noopener noreferrer"&gt;&lt;code&gt;web-client/app.js&lt;/code&gt;&lt;/a&gt;&amp;nbsp;accepts only the expected version, catalog, surface, component types, and HTTPS image origins. Generated values enter the DOM through&amp;nbsp;&lt;code&gt;textContent&lt;/code&gt;; no A2UI payload can provide executable JavaScript:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;const allowedComponents = new Set([
  "Column", "Row", "List", "Card", "Text", "Image", "Button", "TextField"
]);
const allowedImageOrigins = new Set(["https://www.oracle.com"]);

if (!allowedComponents.has(component.component)) {
  throw new Error(`A2UI component not allowed: ${component.component}`);
}

const url = new URL(component.url);
if (url.protocol !== "https:" || !allowedImageOrigins.has(url.origin)) {
  throw new Error(`A2UI image origin not allowed: ${url.origin}`);
}

name.textContent = `${recommendation.sku} · ${recommendation.productName}`;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The application does not present generic, application-authored follow-up choices. Each selectable card is a concrete recommendation produced by&amp;nbsp;&lt;code&gt;STOCKOUT_TRANSFER_RECOMMENDATION_V&lt;/code&gt;, and the only write action is to approve that exact transfer with an operator note or cancel it.&lt;/p&gt;

&lt;h2&gt;The A2A Agent Explicitly Advertises A2UI Support&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The adapter publishes a dynamically generated agent card at&amp;nbsp;&lt;code&gt;GET /.well-known/agent-card.json&lt;/code&gt;. Its A2A protocol version and A2UI extension are separate declarations, so a host can discover both the communication contract and the supported UI capability before invoking the agent.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;build_agent_card()&lt;/code&gt;&amp;nbsp;in&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/gemini-enterprise-a2a/main.py" rel="noopener noreferrer"&gt;&lt;code&gt;gemini-enterprise-a2a/main.py&lt;/code&gt;&lt;/a&gt;&amp;nbsp;generates a card equivalent to this JSON representation:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  "capabilities": {
    "streaming": true,
    "extensions": [
      {
        "uri": "https://a2ui.org/a2a-extension/a2ui/v0.8",
        "required": false,
        "params": {
          "supportedCatalogIds": [
            "https://a2ui.org/specification/v0_8/standard_catalog_definition.json"
          ]
        }
      }
    ]
  },
  "protocolVersion": "0.3.0"
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;protocolVersion&lt;/code&gt;&amp;nbsp;identifies A2A v0.3. The extension URI advertises A2UI v0.8, and&amp;nbsp;&lt;code&gt;supportedCatalogIds&lt;/code&gt;&amp;nbsp;identifies the standard component catalog the agent can produce. Because&amp;nbsp;&lt;code&gt;required&lt;/code&gt;&amp;nbsp;is&amp;nbsp;&lt;code&gt;false&lt;/code&gt;, the extension is optional for the interaction. Inspect a local card with&amp;nbsp;&lt;code&gt;curl -s http://127.0.0.1:3002/.well-known/agent-card.json&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Who Decides Whether to Return A2UI?&lt;/h3&gt;

&lt;p&gt;Advertising A2UI support does not require every response to contain a UI. An agent can choose text, A2UI, or both according to the request, workflow state, host capabilities, and product policy. The host must still validate every A2UI message against the advertised version and catalog.&lt;/p&gt;

&lt;p&gt;This application makes that choice deterministically rather than asking an LLM. In&amp;nbsp;&lt;code&gt;SupplyChainExecutor.execute()&lt;/code&gt;&amp;nbsp;in&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/gemini-enterprise-a2a/main.py" rel="noopener noreferrer"&gt;&lt;code&gt;gemini-enterprise-a2a/main.py&lt;/code&gt;&lt;/a&gt;, a normal request builds the review while a returned&amp;nbsp;&lt;code&gt;userAction&lt;/code&gt;&amp;nbsp;follows the action path:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;action = _extract_user_action(context)
if action:
    parts = await _handle_action(action, access_profile)
else:
    # Retrieve recommendations and return the A2UI review.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A normal request returns text plus an A2UI review surface. An approve or cancel&amp;nbsp;&lt;code&gt;userAction&lt;/code&gt;&amp;nbsp;returns text plus a smaller A2UI result surface. A host that does not render the optional extension can still use the text part, and an error path returns text only.&lt;/p&gt;

&lt;h3&gt;Map Oracle Database Results into Gemini Enterprise A2UI&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/gemini-enterprise-a2a/main.py" rel="noopener noreferrer"&gt;&lt;code&gt;gemini-enterprise-a2a/main.py&lt;/code&gt;&lt;/a&gt;&amp;nbsp;calls the Java service and receives the database-backed recommendation dictionaries:&lt;code&gt;review = await asyncio.to_thread( _post_agent, "/api/reviews", { "minimumStockoutRisk": minimum_risk, "maximumRows": maximum_rows, "accessProfile": access_profile, }, )&lt;/code&gt;
&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/gemini-enterprise-a2a/a2ui_payloads.py" rel="noopener noreferrer"&gt;&lt;code&gt;gemini-enterprise-a2a/a2ui_payloads.py&lt;/code&gt;&lt;/a&gt;&amp;nbsp;maps those values into approved A2UI components:&lt;code&gt;components.extend([ _component( card_id, "Card", {"child": content_id}, ), # Additional approved components are omitted. _text( f"route-{index}", f"{recommendation['sourceLocationCode']} to " f"{recommendation['targetLocationCode']}: " f"{recommendation['recommendedTransferQuantity']} units", ), ])&lt;/code&gt;
&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/gemini-enterprise-a2a/main.py" rel="noopener noreferrer"&gt;&lt;code&gt;gemini-enterprise-a2a/main.py&lt;/code&gt;&lt;/a&gt;&amp;nbsp;packages the messages as A2UI DataParts in the A2A response:&lt;code&gt;parts = [ Part(root=TextPart( text=f"Showing {len(review['recommendations'])} recommendations." )), *(create_a2ui_part(message) for message in messages), ]&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpykgojr2fig1jdzb71am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpykgojr2fig1jdzb71am.png" alt=" " width="800" height="682"&gt;&lt;/a&gt;&lt;/p&gt;





&lt;h2&gt;Optionally Stream A2UI and Agent State with AG-UI&lt;/h2&gt;

&lt;p&gt;The standalone frontend uses AG-UI because it needs incremental run state and tool activity as well as A2UI. The following excerpt from&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/agent-service/src/main/java/com/oracle/demo/interactiveai/AguiRunService.java" rel="noopener noreferrer"&gt;&lt;code&gt;agent-service/src/main/java/com/oracle/demo/interactiveai/AguiRunService.java&lt;/code&gt;&lt;/a&gt;&amp;nbsp;emits AG-UI events over Server-Sent Events and carries each A2UI envelope in a&amp;nbsp;&lt;code&gt;CUSTOM&lt;/code&gt;&amp;nbsp;event:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;send(output, Map.of(
    "type", "TOOL_CALL_START",
    "toolCallId", toolCallId,
    "toolCallName", "find-stockout-transfer-recommendations"));

send(output, Map.of(
    "type", "STATE_SNAPSHOT",
    "snapshot", Map.of("status", "AWAITING_APPROVAL")));

send(output, Map.of(
    "type", "CUSTOM",
    "name", "a2ui.message",
    "value", envelope));&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The frontend can render progress before the full recommendation set arrives and correlate arguments and results with one&amp;nbsp;&lt;code&gt;toolCallId&lt;/code&gt;. The standalone&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/web-client/app.js" rel="noopener noreferrer"&gt;&lt;code&gt;web-client/app.js&lt;/code&gt;&lt;/a&gt;&amp;nbsp;validates allowlisted A2UI state and renders this exchange's HTML rather than acting as a general-purpose A2UI renderer.&lt;/p&gt;





&lt;h2&gt;Add a Supply-Chain Dashboard with MCP Apps&lt;/h2&gt;

&lt;p&gt;The web application at&amp;nbsp;&lt;code&gt;http://127.0.0.1:8080&lt;/code&gt;&amp;nbsp;demonstrates AG-UI and A2UI. The separate&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit/mcp-app" rel="noopener noreferrer"&gt;&lt;code&gt;mcp-app/&lt;/code&gt;&lt;/a&gt;&amp;nbsp;package is a real MCP App demonstration that can run in the official MCP Apps basic host without a third-party account.&lt;/p&gt;

&lt;p&gt;The following abridged model-visible dashboard tool is defined in&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/mcp-app/server.ts" rel="noopener noreferrer"&gt;&lt;code&gt;mcp-app/server.ts&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;registerAppTool(server, "show-inventory-transfer-dashboard", {
  title: "Show inventory transfer dashboard",
  inputSchema: {
    minimumStockoutRisk: z.number().min(0).max(100),
    maximumRows: z.number().int().min(1).max(50)
  },
  _meta: {
    ui: { resourceUri: "ui://oracle-supply-chain/inventory-exchange-v2" }
  }
}, async ({ minimumStockoutRisk, maximumRows }) =&amp;gt; {
  const review =
      await loadGovernedReview(minimumStockoutRisk, maximumRows);
  return {
    structuredContent: {
      recommendations: review.recommendations,
      source: "oracle-db-mcp-java-toolkit"
    },
    _meta: { approvalId: review.approvalId }
  };
});

registerAppTool(server, "approve-inventory-transfer", {
  inputSchema: {
    approvalId: z.string().uuid(),
    recommendationId: z.string(),
    approvalNotes: z.string().min(10).max(500)
  },
  _meta: { ui: { visibility: ["app"] } }
}, approveExactReviewedTransfer);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The same file registers the HTML as an MCP App resource and supplies the sandbox policy:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;registerAppResource(
  server,
  resourceUri,
  resourceUri,
  { mimeType: RESOURCE_MIME_TYPE },
  async () =&amp;gt; ({
    contents: [{
      uri: resourceUri,
      mimeType: RESOURCE_MIME_TYPE,
      text: await readFile(
        path.join(import.meta.dirname, "dist", "mcp-app.html"),
        "utf8"
      ),
      _meta: {
        ui: {
          csp: {
            connectDomains: [],
            resourceDomains: ["https://www.oracle.com"]
          }
        }
      }
    }]
  })
);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The model-visible tool calls&amp;nbsp;&lt;code&gt;/api/reviews&lt;/code&gt;&amp;nbsp;on the Java service. The service invokes the Toolkit's allowlisted read tool and binds a short-lived approval handle to the exact returned rows. The handle travels in widget-only result metadata rather than model-visible content. Selecting a card calls&amp;nbsp;&lt;code&gt;updateModelContext&lt;/code&gt;; approving or canceling calls an app-only tool that the model cannot invoke. The iframe receives neither wallet files nor database credentials.&lt;/p&gt;

&lt;p&gt;The following abridged excerpt from&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/mcp-app/src/mcp-app.ts" rel="noopener noreferrer"&gt;&lt;code&gt;mcp-app/src/mcp-app.ts&lt;/code&gt;&lt;/a&gt;&amp;nbsp;shows the iframe requesting its app-only approval tool through the host bridge:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;const result = await app.callServerTool({
  name: "approve-inventory-transfer",
  arguments: {
    approvalId,
    recommendationId: selectedRecommendation.recommendationId,
    approvalNotes: notes
  }
});

const payload = result.structuredContent as { transferId?: number };
void app.updateModelContext({
  content: [{
    type: "text",
    text: `The user approved transfer ${payload.transferId}.`
  }]
});&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;What Does “Sandboxed Iframe” Mean for an MCP App?&lt;/h3&gt;

&lt;p&gt;In a compatible host that implements the MCP Apps security model, developer-built HTML and JavaScript is loaded as a&amp;nbsp;&lt;code&gt;ui://&lt;/code&gt;&amp;nbsp;resource in an isolated iframe rather than inserted into the host page. The intended boundary prevents the app from reading or modifying the host DOM, cookies, or local storage; navigating the parent page; or executing code in the host context.&lt;/p&gt;

&lt;p&gt;Those protections depend on the host correctly implementing and enforcing separate origins, iframe sandbox flags, Content Security Policy, Permissions Policy, and a validated JSON-RPC&amp;nbsp;&lt;code&gt;postMessage&lt;/code&gt;&amp;nbsp;bridge. MCP Apps metadata requests network, resource, and device permissions; it does not independently enforce the browser boundary. Review the specific host's implementation and current documentation before relying on a capability or restriction.&lt;/p&gt;

&lt;p&gt;The sandbox provides browser isolation, not application authorization. The MCP server must still authenticate the actor, validate every tool input, enforce authorization, and protect downstream transactions.&lt;/p&gt;

&lt;p&gt;The following metadata from&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/mcp-app/server.ts" rel="noopener noreferrer"&gt;&lt;code&gt;mcp-app/server.ts&lt;/code&gt;&lt;/a&gt;&amp;nbsp;keeps network requests disabled, permits the Oracle logo as a static image from one official origin, and keeps approval callable by the app rather than the model.&amp;nbsp;&lt;code&gt;connectDomains&lt;/code&gt;&amp;nbsp;governs fetch, XHR, and WebSocket destinations;&amp;nbsp;&lt;code&gt;resourceDomains&lt;/code&gt;&amp;nbsp;governs images, scripts, styles, fonts, and media:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;_meta: {
  ui: {
    csp: {
      connectDomains: [],
      resourceDomains: ["https://www.oracle.com"]
    }
  }
}

// Approval tool metadata
_meta: { ui: { visibility: ["app"] } }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;See the&amp;nbsp;&lt;a href="https://modelcontextprotocol.io/extensions/apps/overview" rel="noopener noreferrer"&gt;MCP Apps overview&lt;/a&gt;&amp;nbsp;and the&amp;nbsp;&lt;a href="https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx" rel="noopener noreferrer"&gt;stable MCP Apps specification&lt;/a&gt;&amp;nbsp;for normative sandbox and bridge behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbwxfqalpgp4awj1unn2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbwxfqalpgp4awj1unn2y.png" alt=" " width="800" height="769"&gt;&lt;/a&gt;&lt;/p&gt;





&lt;h2&gt;Bind Human Approval to One Exact Transfer&lt;/h2&gt;

&lt;p&gt;When the read completes, the agent service issues a short-lived approval ID bound to the actor and an immutable map of every returned recommendation. The browser posts only the approval ID, recommendation ID, and notes. It cannot replace the product, source, target, or quantity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwvt20dhoha4ufred5tmr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwvt20dhoha4ufred5tmr.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The write path reserves a transfer ID from a sequence and then calls an input-only procedure. The following excerpt from&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/database/setup.sql" rel="noopener noreferrer"&gt;&lt;code&gt;database/setup.sql&lt;/code&gt;&lt;/a&gt;&amp;nbsp;shows how Oracle locks the source and target positions, recomputes surplus and shortage, and rejects a quantity greater than the current safe amount:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;v_safe_transfer_qty := LEAST(v_source_surplus, v_target_shortage);

IF p_transfer_qty &amp;gt; v_safe_transfer_qty THEN
  RAISE_APPLICATION_ERROR(
    -20007,
    'Recommendation is stale; current safe transfer quantity is ' ||
    TO_CHAR(v_safe_transfer_qty));
END IF;

INSERT INTO inventory_transfers (...);
UPDATE inventory_positions
   SET reserved_qty = reserved_qty + p_transfer_qty
 WHERE product_id = p_product_id
   AND location_id = p_source_location_id;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The insert and inventory reservation are one tool statement and one transaction. Canceling consumes the pending approval without invoking the write. Reuse, an actor change, an unknown recommendation, invalid notes, or stale inventory causes rejection.&lt;/p&gt;

&lt;p&gt;Auditable AI agent answers need more than explanatory prose: the UI preserves the Toolkit source label and database-calculated inputs, while an approved action returns a database transfer ID and durable audit state.&lt;/p&gt;





&lt;h2&gt;Run the Application Locally End to End&lt;/h2&gt;

&lt;p&gt;This local reference run shows how to test a database-backed AI application locally before deployment. It validates the shared database, standalone Toolkit, Java service, AG-UI stream, A2UI renderer, and MCP App before the following sections connect those same components to external conversational hosts. The browser places the database-calculated recommendation and approval controls beside the event stream so tool calling and state synchronization remain visible without an oversized full-page screenshot.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cd a2ui_mcpapps_mcptoolkit
cp .env.example .env
# Set DB_URL, TNS_ADMIN, DB_USERNAME, and DB_PASSWORD.

cd agent-service
./setup-database.sh
./test.sh

# Terminal 1: standalone Toolkit
cd ../oracle-db-mcp-toolkit
./run.sh full

# Terminal 2: Java service and browser
cd ../agent-service
./run.sh

# Open http://127.0.0.1:8080&lt;/code&gt;&lt;/pre&gt;

&lt;ol start="0"&gt;
&lt;li&gt;
&lt;strong&gt;Create Oracle AI Database 26ai.&lt;/strong&gt;&amp;nbsp;Use a cloud service or a local installation by following the&amp;nbsp;&lt;a href="https://www.oracle.com/database/free/get-started/" rel="noopener noreferrer"&gt;Oracle AI Database 26ai quick start&lt;/a&gt;.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Prepare the database.&lt;/strong&gt;&amp;nbsp;Run&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/agent-service/setup-database.sh" rel="noopener noreferrer"&gt;&lt;code&gt;agent-service/setup-database.sh&lt;/code&gt;&lt;/a&gt;&amp;nbsp;to create the sample inventory objects, recommendation view, transfer sequence, and approval procedure.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Start the Toolkit.&lt;/strong&gt;&amp;nbsp;In one terminal, run&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/oracle-db-mcp-toolkit/run.sh" rel="noopener noreferrer"&gt;&lt;code&gt;oracle-db-mcp-toolkit/run.sh full&lt;/code&gt;&lt;/a&gt;. It exposes the allowlisted tools at an authenticated TLS MCP endpoint.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Start the application.&lt;/strong&gt;&amp;nbsp;In another terminal, run&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/agent-service/run.sh" rel="noopener noreferrer"&gt;&lt;code&gt;agent-service/run.sh&lt;/code&gt;&lt;/a&gt;. Its HTTP MCP client connects to the Toolkit, verifies its identity and exact allowlist, and exposes the API and web client.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Open the inventory exchange.&lt;/strong&gt;&amp;nbsp;Browse to&amp;nbsp;&lt;code&gt;http://127.0.0.1:8080&lt;/code&gt;.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Run the analysis.&lt;/strong&gt;&amp;nbsp;Set minimum stockout risk to&amp;nbsp;&lt;code&gt;50&lt;/code&gt;, maximum rows to&amp;nbsp;&lt;code&gt;10&lt;/code&gt;, and submit.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Retrieve governed recommendations.&lt;/strong&gt;&amp;nbsp;The Toolkit binds the inputs and calls&amp;nbsp;&lt;code&gt;find-stockout-transfer-recommendations&lt;/code&gt;.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Inspect the streamed result.&lt;/strong&gt;&amp;nbsp;AG-UI shows lifecycle and tool activity while A2UI renders the recommendation cards and approval controls.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Approve or cancel.&lt;/strong&gt;&amp;nbsp;Select one returned recommendation, enter notes, and choose the explicit action.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Commit through MCP.&lt;/strong&gt;&amp;nbsp;Oracle locks and revalidates both positions, writes the audit row, and reserves source inventory atomically.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Run the smoke test.&lt;/strong&gt;&amp;nbsp;From another terminal, run&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/scripts/smoke-test.sh" rel="noopener noreferrer"&gt;&lt;code&gt;scripts/smoke-test.sh&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Open the MCP App locally.&lt;/strong&gt;&amp;nbsp;Run&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/mcp-app/run.sh" rel="noopener noreferrer"&gt;&lt;code&gt;mcp-app/run.sh&lt;/code&gt;&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/mcp-app/run-basic-host.sh" rel="noopener noreferrer"&gt;&lt;code&gt;mcp-app/run-basic-host.sh&lt;/code&gt;&lt;/a&gt;, open&amp;nbsp;&lt;code&gt;http://127.0.0.1:8082&lt;/code&gt;, and invoke&amp;nbsp;&lt;code&gt;show-inventory-transfer-dashboard&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;





&lt;h2&gt;Full Setup and Runtime Walkthroughs for A2UI and MCP Apps in ChatGPT, Claude, and Gemini Enterprise&lt;/h2&gt;

&lt;p&gt;The following walkthroughs show the portable MCP App in ChatGPT, Claude, and Gemini Enterprise, followed by native A2UI over A2A in Gemini Enterprise. These products demonstrate the two UI approaches without defining or limiting which capable hosts can implement them.&lt;/p&gt;





&lt;h2&gt;Run the MCP App in ChatGPT&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Author verification:&lt;/strong&gt;&amp;nbsp;The capability and setup claims in this section were checked against OpenAI's official&amp;nbsp;&lt;a href="https://developers.openai.com/plugins/build/chatgpt-ui" rel="noopener noreferrer"&gt;ChatGPT UI&lt;/a&gt;,&amp;nbsp;&lt;a href="https://developers.openai.com/plugins/build/auth" rel="noopener noreferrer"&gt;authentication&lt;/a&gt;, and&amp;nbsp;&lt;a href="https://developers.openai.com/plugins/deploy/connect-chatgpt" rel="noopener noreferrer"&gt;connection and testing&lt;/a&gt;&amp;nbsp;documentation on August 20, 2026. Product interfaces and terminology can change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT can discover the bounded MCP tool, call it with validated limits, load the&amp;nbsp;&lt;code&gt;ui://&lt;/code&gt;&amp;nbsp;resource, and render the Oracle Database MCP Java Toolkit result inline.&lt;/p&gt;

&lt;p&gt;OpenAI's current flow calls the host integration a&amp;nbsp;&lt;em&gt;plugin&lt;/em&gt;&amp;nbsp;backed by an MCP connection. The server exposes the component as a&amp;nbsp;&lt;code&gt;ui://&lt;/code&gt;&amp;nbsp;resource with media type&amp;nbsp;&lt;code&gt;text/html;profile=mcp-app&lt;/code&gt;, while its public Streamable HTTP endpoint normally ends in&amp;nbsp;&lt;code&gt;/mcp&lt;/code&gt;. This repository already implements that resource, tool metadata, host bridge, read-only dashboard tool, and app-only approval and rejection tools.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ChatGPT plugin / MCP connection
  |  public HTTPS Streamable HTTP: /mcp
  v
TypeScript MCP App server
  |  ui:// resource + app-only action tools
  v
Java approval service -&amp;gt; Oracle DB MCP Toolkit -&amp;gt; Oracle AI Database&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Secure deployment and setup&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Authentication boundary:&lt;/strong&gt;&amp;nbsp;keep the service private by default. Use synthetic data for any explicitly authorized anonymous read-only demonstration, and require OAuth 2.1 with issuer, audience, expiry, and scope validation before exposing customer-specific data or write actions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Set the required environment variables documented at the top of&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/deploy/gcp/deploy-chatgpt-mcp.sh" rel="noopener noreferrer"&gt;&lt;code&gt;deploy/gcp/deploy-chatgpt-mcp.sh&lt;/code&gt;&lt;/a&gt;, then run that script from macOS or Linux to build and deploy the private read-only service. It reads the wallet and password from Secret Manager.&lt;/li&gt;



&lt;li&gt;With a Google identity token, verify&amp;nbsp;&lt;code&gt;/health&lt;/code&gt;,&amp;nbsp;&lt;code&gt;tools/list&lt;/code&gt;,&amp;nbsp;&lt;code&gt;resources/read&lt;/code&gt;, and a read-only&amp;nbsp;&lt;code&gt;tools/call&lt;/code&gt;. Confirm that anonymous access still returns 403.&lt;/li&gt;



&lt;li&gt;Choose the access gate. Keep&amp;nbsp;&lt;code&gt;MCP_WRITES_ENABLED=false&lt;/code&gt;&amp;nbsp;for a synthetic-data rendering demonstration. For durable use or any approval action, configure an established OAuth 2.1 provider.&lt;/li&gt;



&lt;li&gt;In ChatGPT, open&amp;nbsp;&lt;em&gt;Settings &amp;gt; Security and login&lt;/em&gt;&amp;nbsp;and enable&amp;nbsp;&lt;em&gt;Developer mode&lt;/em&gt;. Workspace policy can control whether this setting is available.&lt;/li&gt;



&lt;li&gt;Open&amp;nbsp;&lt;em&gt;Plugins&lt;/em&gt;, select the plus button, name the plugin&amp;nbsp;&lt;em&gt;Supply-Chain Inventory Exchange&lt;/em&gt;, choose the configured authentication method, and enter the deployment URL ending in&amp;nbsp;&lt;code&gt;/mcp&lt;/code&gt;.&lt;/li&gt;



&lt;li&gt;Acknowledge the custom-MCP warning, create and connect the plugin, then inspect its metadata. Discovery must show only&amp;nbsp;&lt;code&gt;show-inventory-transfer-dashboard&lt;/code&gt;&amp;nbsp;with&amp;nbsp;&lt;code&gt;minimumStockoutRisk&lt;/code&gt;&amp;nbsp;and&amp;nbsp;&lt;code&gt;maximumRows&lt;/code&gt;&amp;nbsp;inputs.&lt;/li&gt;



&lt;li&gt;Start a new conversation, add&amp;nbsp;&lt;em&gt;Supply-Chain Inventory Exchange&lt;/em&gt;&amp;nbsp;from the tools menu, and ask: “Show the inventory transfer dashboard for products with a minimum stockout risk of 70, limited to 3 recommendations.”&lt;/li&gt;



&lt;li&gt;Verify that ChatGPT calls the tool with&amp;nbsp;&lt;code&gt;minimumStockoutRisk=70&lt;/code&gt;&amp;nbsp;and&amp;nbsp;&lt;code&gt;maximumRows=3&lt;/code&gt;, renders the embedded component, and labels the result as live data from the Toolkit.&lt;/li&gt;



&lt;li&gt;Refresh the plugin connection whenever tool or resource metadata changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fny7q0v7dzomk9i6194b3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fny7q0v7dzomk9i6194b3.png" alt=" " width="800" height="814"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnwm2bpbuzbqlbukwu4af.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnwm2bpbuzbqlbukwu4af.png" alt=" " width="800" height="1381"&gt;&lt;/a&gt;&lt;/p&gt;





&lt;h2&gt;Run the MCP App in Claude&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Author verification:&lt;/strong&gt;&amp;nbsp;The capability and setup claims in this section were checked against Anthropic's official&amp;nbsp;&lt;a href="https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp" rel="noopener noreferrer"&gt;remote MCP connector&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="https://support.claude.com/en/articles/13454812-use-interactive-connectors-in-claude" rel="noopener noreferrer"&gt;interactive connector&lt;/a&gt;&amp;nbsp;documentation on August 20, 2026. Product interfaces and terminology can change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude can discover the custom connector, call its bounded interactive tool, receive the Oracle Database MCP Java Toolkit result, and render the same&amp;nbsp;&lt;code&gt;ui://&lt;/code&gt;&amp;nbsp;MCP App. Because Claude connects from Anthropic's cloud, it needs a securely exposed remote endpoint that meets the host's network and authentication requirements.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Claude interactive connector
  |  public remote MCP endpoint: /mcp
  v
Same TypeScript MCP App server
  |  ui:// resource + one bounded read-only tool
  v
Java approval service -&amp;gt; Oracle DB MCP Toolkit -&amp;gt; Oracle AI Database&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Setup and invocation steps&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Deploy the same Streamable HTTP MCP App service used by ChatGPT with an appropriate authentication boundary.&lt;/li&gt;



&lt;li&gt;In Claude, open&amp;nbsp;&lt;em&gt;Settings &amp;gt; Connectors&lt;/em&gt;, select&amp;nbsp;&lt;em&gt;Add &amp;gt; Add custom connector&lt;/em&gt;, provide a descriptive name, and enter the remote URL ending in&amp;nbsp;&lt;code&gt;/mcp&lt;/code&gt;.&lt;/li&gt;



&lt;li&gt;Open the connector detail page. Confirm that it is connected and exposes exactly one interactive tool,&amp;nbsp;&lt;em&gt;Show inventory transfer dashboard&lt;/em&gt;. Choose whether Claude may invoke it automatically, ask each time, or never.&lt;/li&gt;



&lt;li&gt;Start a new chat, open the plus menu, enable&amp;nbsp;&lt;em&gt;Supply-Chain Inventory Exchange&lt;/em&gt;, and allow connector search if Claude presents that separate discovery opt-in.&lt;/li&gt;



&lt;li&gt;Ask:&amp;nbsp;&lt;q&gt;Show the inventory transfer dashboard for products with a minimum stockout risk of 70, limited to 3 recommendations.&lt;/q&gt;
&lt;/li&gt;



&lt;li&gt;Verify the model-visible tool result and inline widget both report the same Toolkit-backed row:&amp;nbsp;&lt;code&gt;WATER-SENSE&lt;/code&gt;,&amp;nbsp;&lt;code&gt;PHX-DC&lt;/code&gt;&amp;nbsp;to&amp;nbsp;&lt;code&gt;SEA-FC&lt;/code&gt;, 42 units, and stockout risk 74.6.&lt;/li&gt;



&lt;li&gt;Require OAuth 2.1 before exposing customer-specific data or registering the approval and rejection tools.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1oule8x08ut1y4jywmgk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1oule8x08ut1y4jywmgk.png" alt=" " width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F00ykt3wf14l8hau1l1oc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F00ykt3wf14l8hau1l1oc.png" alt=" " width="800" height="727"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxvl4c3rz3ixhf4mrploc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxvl4c3rz3ixhf4mrploc.png" alt=" " width="800" height="728"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4aw0sf5urr3q4lhmndbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4aw0sf5urr3q4lhmndbg.png" alt=" " width="800" height="728"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1muvr5pa3ou9od95dr1z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1muvr5pa3ou9od95dr1z.png" alt=" " width="800" height="1338"&gt;&lt;/a&gt;&lt;/p&gt;





&lt;h2&gt;Run the MCP App in Gemini Enterprise&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Author verification:&lt;/strong&gt;&amp;nbsp;The capability and setup claims in this section were checked against Google Cloud's official&amp;nbsp;&lt;a href="https://docs.cloud.google.com/gemini/enterprise/docs/connectors/custom-mcp-server/set-up-custom-mcp-server" rel="noopener noreferrer"&gt;custom MCP server data store&lt;/a&gt;&amp;nbsp;documentation on August 20, 2026. Product interfaces and terminology can change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Gemini Enterprise can load the same&amp;nbsp;&lt;code&gt;ui://&lt;/code&gt;&amp;nbsp;dashboard through a Custom MCP Server data store. This is separate from its native A2UI/A2A agent: the MCP path discovers a tool and loads sandboxed web content, while the A2UI path receives declarative component messages and renders native controls.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Gemini Enterprise
  |  private Custom MCP Server connector
  |  Streamable HTTP + OAuth
  v
TypeScript MCP App server
  |  ui:// inventory dashboard
  v
Java service -&amp;gt; Oracle DB MCP Toolkit -&amp;gt; Oracle AI Database&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Private connector setup&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Set the required environment variables documented at the top of&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/deploy/gcp/deploy-gemini-enterprise-mcp.sh" rel="noopener noreferrer"&gt;&lt;code&gt;deploy/gcp/deploy-gemini-enterprise-mcp.sh&lt;/code&gt;&lt;/a&gt;, then run that script from macOS or Linux. It keeps Cloud Run private, disables MCP writes, and grants invocation only to the Discovery Engine service agent.&lt;/li&gt;



&lt;li&gt;Create a Google OAuth web client with the authorized redirect URI&amp;nbsp;&lt;code&gt;https://vertexaisearch.cloud.google.com/oauth-redirect&lt;/code&gt;. Keep the client secret outside Git, screenshots, logs, and model context.&lt;/li&gt;



&lt;li&gt;Create a&amp;nbsp;&lt;em&gt;Custom MCP Server&lt;/em&gt;&amp;nbsp;data store. Enter the private service URL ending in&amp;nbsp;&lt;code&gt;/mcp&lt;/code&gt;, authorization URL&amp;nbsp;&lt;code&gt;https://accounts.google.com/o/oauth2/auth&lt;/code&gt;, token URL&amp;nbsp;&lt;code&gt;https://oauth2.googleapis.com/token&lt;/code&gt;, and authorization parameter&amp;nbsp;&lt;code&gt;&amp;amp;access_type=offline&lt;/code&gt;.&lt;/li&gt;



&lt;li&gt;Request&amp;nbsp;&lt;code&gt;openid email profile https://www.googleapis.com/auth/cloud-platform&lt;/code&gt;, enable PKCE, enable HTTP Basic authentication, select&amp;nbsp;&lt;em&gt;Login&lt;/em&gt;, and complete the Google authorization flow.&lt;/li&gt;



&lt;li&gt;Name the connector&amp;nbsp;&lt;em&gt;Oracle Supply-Chain MCP App&lt;/em&gt;, connect it to the target Gemini Enterprise application, and wait until its status is&amp;nbsp;&lt;em&gt;Active&lt;/em&gt;.&lt;/li&gt;



&lt;li&gt;Open&amp;nbsp;&lt;em&gt;Actions&lt;/em&gt;, select&amp;nbsp;&lt;em&gt;Reload custom actions&lt;/em&gt;, enable&amp;nbsp;&lt;code&gt;show-inventory-transfer-dashboard&lt;/code&gt;, and verify that no broader SQL or write tool is model-visible.&lt;/li&gt;



&lt;li&gt;In the Gemini Enterprise application, enable the connector and ask:&amp;nbsp;&lt;q&gt;Show the inventory transfer dashboard for products with a minimum stockout risk of 70, limited to 3 recommendations.&lt;/q&gt;
&lt;/li&gt;



&lt;li&gt;Confirm that the tool result and sandboxed widget agree on the governed recommendation and display the read-only boundary. The native A2UI agent is not involved in this path.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frzno3kqd81c92rstzio0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frzno3kqd81c92rstzio0.png" alt=" " width="798" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjq1uvararzxktimdfcrv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjq1uvararzxktimdfcrv.png" alt=" " width="799" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcutpnd4sobthg2e8ih3j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcutpnd4sobthg2e8ih3j.png" alt=" " width="800" height="759"&gt;&lt;/a&gt;&lt;/p&gt;





&lt;h2&gt;Run Native A2UI in Gemini Enterprise&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Author verification:&lt;/strong&gt;&amp;nbsp;The capability and setup claims in this section were checked against Google Cloud's official&amp;nbsp;&lt;a href="https://docs.cloud.google.com/gemini/enterprise/docs/a2ui-agents/register-and-manage-an-a2ui-agent" rel="noopener noreferrer"&gt;A2UI registration&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="https://docs.cloud.google.com/gemini/enterprise/docs/a2ui-agents/tutorial-host-agent-cloud-run" rel="noopener noreferrer"&gt;Cloud Run tutorial&lt;/a&gt;&amp;nbsp;documentation on August 20, 2026. Product interfaces and terminology can change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Gemini Enterprise is the A2UI-capable host used in this reference application. Its custom-agent path uses A2A as the conversation transport and A2UI as declarative UI cargo. This implementation targets A2A v0.3 and A2UI v0.8, while the standalone browser consumes A2UI v0.9.1 envelopes. This is one implementation example, not a coupling between A2UI and Gemini Enterprise.&lt;/p&gt;

&lt;h3&gt;Separate A2A Transport from A2UI Presentation&lt;/h3&gt;

&lt;p&gt;In this repository,&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/gemini-enterprise-a2a/main.py" rel="noopener noreferrer"&gt;&lt;code&gt;gemini-enterprise-a2a/main.py&lt;/code&gt;&lt;/a&gt;&amp;nbsp;co-locates the A2A endpoint, request executor, Java API client, and deterministic A2UI v0.8 builder in one process. That is a compact implementation choice, not a protocol requirement. A2A carries messages, tasks, artifacts, and DataParts between Gemini Enterprise and the remote agent; A2UI is the declarative presentation placed in those DataParts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foz6i4wzx8d4wrzddts51.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foz6i4wzx8d4wrzddts51.png" alt=" " width="799" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fah35veo5yjn9zk6nbamn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fah35veo5yjn9zk6nbamn.png" alt=" " width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The agent card shown in the A2UI implementation section advertises the A2UI v0.8 extension and standard catalog that this co-located builder produces. A decoupled implementation would advertise the same external contract.&lt;/p&gt;

&lt;h3&gt;Follow the Native A2A/A2UI Path&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;Gemini Enterprise
  |  A2A v0.3 JSON-RPC
  |  A2UI v0.8 DataParts
  v
gemini-enterprise-a2a
  |  POST /api/reviews, /api/approve, /api/reject
  v
Java agent service
  |  Oracle Database MCP Java Toolkit
  v
Oracle AI Database&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The adapter emits deterministic&amp;nbsp;&lt;code&gt;beginRendering&lt;/code&gt;,&amp;nbsp;&lt;code&gt;surfaceUpdate&lt;/code&gt;, and&amp;nbsp;&lt;code&gt;dataModelUpdate&lt;/code&gt;&amp;nbsp;messages containing native cards, approval notes, and one button per exact recommendation. Gemini Enterprise resolves the button context into a v0.8&amp;nbsp;&lt;code&gt;userAction&lt;/code&gt;; the adapter forwards only the approval handle, recommendation ID, and notes. The Java service recovers the immutable recommendation and Oracle revalidates it under locks. Every host-specific adapter shares this governed workflow and transaction without sharing a UI protocol.&lt;/p&gt;

&lt;p&gt;The following excerpt from&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/blob/main/a2ui_mcpapps_mcptoolkit/gemini-enterprise-a2a/a2ui_payloads.py" rel="noopener noreferrer"&gt;&lt;code&gt;gemini-enterprise-a2a/a2ui_payloads.py&lt;/code&gt;&lt;/a&gt;&amp;nbsp;returns the ordered A2UI v0.8 messages that the host renders:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;return [
    {
        "beginRendering": {
            "surfaceId": surface_id,
            "root": "root",
        }
    },
    {
        "surfaceUpdate": {
            "surfaceId": surface_id,
            "components": components,
        }
    },
    {
        "dataModelUpdate": {
            "surfaceId": surface_id,
            "contents": [{
                "key": "approvalNotes",
                "valueString": (
                    "Approve the database-recommended transfer "
                    "to reduce stockout exposure."
                ),
            }],
        }
    },
]&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Deploy the Gemini adapter on Google Cloud&lt;/h3&gt;

&lt;p&gt;Gemini Enterprise requires a reachable HTTPS A2A endpoint; it does not require the agent to run on a Compute Engine VM. Cloud Run supplies managed HTTPS and can reach a private Oracle AI Database endpoint through Direct VPC egress when required.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Gemini Enterprise: Inventory System
  |  managed HTTPS
  v
Cloud Run: A2A/A2UI adapter
  |  IAM-authenticated adapter :8080
  |  loopback Java service :8081
  |  authenticated Streamable HTTP
  v
Oracle Database MCP Java Toolkit service
  v
Oracle AI Database private service&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The Cloud Run image contains the adapter, Java service, and pinned Toolkit but no secret. Secret Manager mounts the Oracle wallet and injects the dedicated database password at runtime. The entrypoint expands the wallet into ephemeral storage, starts Java only on loopback, waits for its health check, and then exposes the A2A adapter on Cloud Run's assigned port.&lt;/p&gt;

&lt;p&gt;The checked-in&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit/deploy/gcp" rel="noopener noreferrer"&gt;&lt;code&gt;deploy/gcp&lt;/code&gt;&lt;/a&gt;&amp;nbsp;scripts build the image with Cloud Build, use a dedicated runtime service account, limit Cloud Run to one instance while approval handles remain in memory, set the final service URL in the agent card, and register that card through the official Discovery Engine&amp;nbsp;&lt;code&gt;assistants/default_assistant/agents&lt;/code&gt;&amp;nbsp;REST resource. The deployed endpoint rejects anonymous calls and grants&amp;nbsp;&lt;code&gt;roles/run.invoker&lt;/code&gt;&amp;nbsp;only to Gemini Enterprise's Discovery Engine service agent. Google's Cloud Run guidance recommends this IAM model for internal clients such as Gemini Enterprise; an OAuth client is needed only when the agent must access Google resources on behalf of the end user.&lt;/p&gt;

&lt;p&gt;The private database endpoint is reached through Cloud Run Direct VPC egress. A VM in the VPC remains a valid alternative, but the protocol and A2UI implementation do not change. Reject anonymous discovery and grant invocation only to the intended Gemini Enterprise service identity.&lt;/p&gt;

&lt;h3&gt;Register, share, and invoke the custom agent&lt;/h3&gt;

&lt;p&gt;Register the deployed agent card with the Gemini Enterprise application in the same project and location, then share the custom agent using the per-agent&amp;nbsp;&lt;code&gt;Agent User&lt;/code&gt;&amp;nbsp;role. Assign the required Gemini Enterprise license to each user; licensing is an account prerequisite, not part of the application architecture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Febgfblo4huqv3z5znn37.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Febgfblo4huqv3z5znn37.png" alt=" " width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Gemini Enterprise web app, the shared agent appears under&amp;nbsp;&lt;em&gt;From your organization&lt;/em&gt;. The prompt&amp;nbsp;&lt;q&gt;Show inventory transfers with a minimum stockout risk of 70, limited to 3 recommendations&lt;/q&gt;&amp;nbsp;invokes the agent over authenticated A2A streaming. Oracle returned the one row in the sample data that met that threshold: a 42-unit&amp;nbsp;&lt;code&gt;WATER-SENSE&lt;/code&gt;&amp;nbsp;transfer from&amp;nbsp;&lt;code&gt;PHX-DC&lt;/code&gt;&amp;nbsp;to&amp;nbsp;&lt;code&gt;SEA-FC&lt;/code&gt;&amp;nbsp;with a stockout-risk score of 74.6. Gemini Enterprise rendered the returned A2UI DataParts as a native card, approval-notes field, exact-transfer approval button, and cancel-without-writing button. The host never loaded the MCP App iframe.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3qixsluo11z8syyiym8x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3qixsluo11z8syyiym8x.png" alt=" " width="800" height="1335"&gt;&lt;/a&gt;&lt;/p&gt;





&lt;h2&gt;Security and Governance Checklist&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use a least-privileged database principal with only the required view and procedure grants.&lt;/li&gt;



&lt;li&gt;Run the Toolkit as a separate Streamable HTTP service with TLS and bearer authentication or OAuth 2.0.&lt;/li&gt;



&lt;li&gt;Enable only purpose-built tools. Do not use&amp;nbsp;&lt;code&gt;-Dtools=*&lt;/code&gt;&amp;nbsp;or unrestricted&amp;nbsp;&lt;code&gt;write-query&lt;/code&gt;.&lt;/li&gt;



&lt;li&gt;Validate and bind every user value; enforce score, row-count, text-length, and timeout limits.&lt;/li&gt;



&lt;li&gt;Bind approval to actor, recommendation data, expiry, and a single use; add durable idempotency for production.&lt;/li&gt;



&lt;li&gt;Revalidate current inventory under locks at the final database boundary.&lt;/li&gt;



&lt;li&gt;Log tool, actor, timestamp, recommendation, approval, and status without logging secrets.&lt;/li&gt;



&lt;li&gt;Allowlist A2UI catalogs and components; treat MCP App content as untrusted and apply sandbox, CSP, and permission controls.&lt;/li&gt;



&lt;li&gt;Keep wallets, passwords, tokens, and client secrets out of the browser, model context, and repository.&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;Production Considerations&lt;/h2&gt;

&lt;p&gt;This reference application uses a transparent single-transfer heuristic. It does not replace supply-network optimization, order management, transportation planning, or warehouse execution. Approval state is stored in process, so production deployments need durable, shared approval and idempotency storage. Remote MCP write actions also require actor-bound OAuth authorization.&lt;/p&gt;

&lt;p&gt;Negotiate A2UI versions and component catalogs explicitly, validate every payload at the host boundary, and test compatibility before upgrading an agent or host. Use TLS, least-privileged service identities, bounded location and value authority, and concurrent-transfer tests while preserving the database transaction as the final execution boundary.&lt;/p&gt;





&lt;h2&gt;References&lt;/h2&gt;

&lt;p&gt;Host product names identify documented examples tested for this article; they do not imply partnership, endorsement, or certification. Host-specific capability and setup claims link to the vendors' canonical documentation below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-for-sustainable-dev/tree/main/a2ui_mcpapps_mcptoolkit" rel="noopener noreferrer"&gt;Source code for this supply-chain reference application&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://github.com/oracle/mcp/blob/main/src/oracle-db-mcp-java-toolkit/README.md" rel="noopener noreferrer"&gt;Oracle Database MCP Java Toolkit README&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.ag-ui.com/introduction" rel="noopener noreferrer"&gt;AG-UI protocol overview&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://a2ui.org/" rel="noopener noreferrer"&gt;A2UI project and specification&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://a2ui.org/quickstart/#step-5-try-it-out" rel="noopener noreferrer"&gt;A2UI quickstart data flow&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://a2ui.org/guides/mcp-apps-in-a2ui/" rel="noopener noreferrer"&gt;A2UI: MCP Apps in A2UI surfaces&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://a2ui.org/guides/a2ui-in-mcp-apps/" rel="noopener noreferrer"&gt;A2UI: dynamic A2UI rendering within MCP Apps&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://southleft.com/insights/ai/a2ui-how-ai-agents-build-real-user-interfaces/" rel="noopener noreferrer"&gt;Southleft: A2UI, How AI Agents Build Real User Interfaces&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/extensions/apps/overview" rel="noopener noreferrer"&gt;MCP Apps overview&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/extensions/apps/build" rel="noopener noreferrer"&gt;Build an MCP App&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://github.com/modelcontextprotocol/ext-apps/" rel="noopener noreferrer"&gt;Official MCP Apps SDK, examples, and basic host&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://cloud.google.com/run/docs/authenticating/service-to-service" rel="noopener noreferrer"&gt;Google Cloud Run service-to-service authentication&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://developers.openai.com/plugins/build/chatgpt-ui" rel="noopener noreferrer"&gt;OpenAI: add UI to an MCP server&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://developers.openai.com/plugins/build/auth" rel="noopener noreferrer"&gt;OpenAI: authenticate plugin MCP servers&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://developers.openai.com/plugins/deploy/connect-chatgpt" rel="noopener noreferrer"&gt;Connect and test a ChatGPT plugin&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp" rel="noopener noreferrer"&gt;Anthropic: get started with custom connectors using remote MCP&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://support.claude.com/en/articles/13454812-use-interactive-connectors-in-claude" rel="noopener noreferrer"&gt;Anthropic: use interactive connectors in Claude&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://support.claude.com/en/articles/11176164-use-connectors-to-extend-claude-s-capabilities" rel="noopener noreferrer"&gt;Anthropic: use connectors to extend Claude's capabilities&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://claude.com/docs/connectors/building/mcp-apps/troubleshooting" rel="noopener noreferrer"&gt;Anthropic: troubleshoot MCP Apps in Claude&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://claude.com/docs/connectors/building/mcp-apps/cross-compatibility" rel="noopener noreferrer"&gt;Anthropic: build cross-platform MCP Apps&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.cloud.google.com/gemini/enterprise/docs/a2ui-agents/register-and-manage-an-a2ui-agent" rel="noopener noreferrer"&gt;Gemini Enterprise: register an A2UI/A2A agent&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.cloud.google.com/gemini/enterprise/docs/register-and-manage-an-a2a-agent" rel="noopener noreferrer"&gt;Gemini Enterprise: register an A2A agent&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.cloud.google.com/run/docs/deploy-a2a-agents" rel="noopener noreferrer"&gt;Google Cloud: deploy A2A agents to Cloud Run with IAM&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.cloud.google.com/gemini/enterprise/docs/a2ui-agents/tutorial-host-agent-cloud-run" rel="noopener noreferrer"&gt;Gemini Enterprise: host an A2UI agent on Cloud Run&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://cloud.google.com/blog/topics/developers-practitioners/guide-to-gemini-enterprise-and-a2ui-integration" rel="noopener noreferrer"&gt;Google Cloud: guide to Gemini Enterprise and A2UI integration&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://github.com/google/A2UI" rel="noopener noreferrer"&gt;Google A2UI source and examples&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://github.com/wadave/agent-a2ui-demo" rel="noopener noreferrer"&gt;A2UI reference implementation linked by Google&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.cloud.google.com/run/docs/configuring/vpc-direct-vpc" rel="noopener noreferrer"&gt;Google Cloud Run Direct VPC egress&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.cloud.google.com/gemini/enterprise/docs/connectors/custom-mcp-server/set-up-custom-mcp-server" rel="noopener noreferrer"&gt;Gemini Enterprise: set up a custom MCP server data store&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.oracle.com/en-us/iaas/Content/database-at-gcp-autonomous/ogadb-task-1-creating-odb-network-oracle-databasegoogle.html" rel="noopener noreferrer"&gt;Oracle Database@Google Cloud ODB network setup&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.oracle.com/en-us/iaas/Content/database-at-gcp/network-topologies.htm" rel="noopener noreferrer"&gt;Oracle Database@Google Cloud network topologies&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/specification/latest" rel="noopener noreferrer"&gt;Model Context Protocol specification&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://a2a-protocol.org/latest/" rel="noopener noreferrer"&gt;Agent2Agent protocol documentation&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://www.oracle.com/database/" rel="noopener noreferrer"&gt;Oracle AI Database&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;Frequently Asked Questions&lt;/h2&gt;

&lt;h3&gt;Does the MCP App replace the web client?&lt;/h3&gt;

&lt;p&gt;No. The custom web client demonstrates AG-UI and A2UI. The separate MCP App renders the same Toolkit-backed recommendations inside a compatible conversational host.&lt;/p&gt;

&lt;h3&gt;Are A2UI and MCP Apps tied to specific host products?&lt;/h3&gt;

&lt;p&gt;No. They are capability-based UI contracts. This project demonstrates Gemini Enterprise as an A2A/A2UI host and as an MCP Apps host, alongside ChatGPT and Claude MCP App examples. The governed service, Toolkit tools, recommendation IDs, and Oracle transaction boundary remain independent of the host.&lt;/p&gt;

&lt;h3&gt;Why not let the agent generate SQL or choose any transfer?&lt;/h3&gt;

&lt;p&gt;A bounded recommendation view and stored procedure make the allowed data, feasibility formula, privileges, locks, transaction, and audit result explicit. Unrestricted SQL or client-selected quantities would expand the attack surface and make execution less deterministic.&lt;/p&gt;

&lt;h3&gt;Where is approval enforced?&lt;/h3&gt;

&lt;p&gt;The agent service binds approval to the authenticated actor and exact returned recommendation. The MCP tool accepts only validated inputs, and Oracle AI Database locks and rechecks current stock before it commits the transfer.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>oracle</category>
      <category>database</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>Building an agent harness that survives production</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Wed, 09 Sep 2026 14:36:13 +0000</pubDate>
      <link>https://dev.to/oracledevs/building-an-agent-harness-that-survives-production-89a</link>
      <guid>https://dev.to/oracledevs/building-an-agent-harness-that-survives-production-89a</guid>
      <description>&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F09%2Fimage-1024x399.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F09%2Fimage-1024x399.png" alt="A model proposes an action, the harness narrows it against policy before it reaches anything real, and evidence travels back from the environment so a completion claim can be checked rather than believed." width="800" height="312"&gt;&lt;/a&gt;Diagram showing the model proposing, the harness authorising, and the environment applying changes.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Put an agent on your refunds queue. It reads the ticket, finds the order, checks the policy, issues the refund. The demo goes beautifully.&lt;/p&gt;

&lt;p&gt;Six weeks later it closes 140 tickets in an afternoon and reports every one resolved. Forty-one of them never reached the payments API. The model wrote “refund issued” because that is what the end of a refund conversation looks like.&lt;/p&gt;

&lt;p&gt;The agent was not confused about the work. Researchers replayed 16,758 coding-agent runs: in two of the systems tested, 60 to 69 per cent of the runs that ended in a wrong answer had already found and edited the right code first (&lt;a href="https://arxiv.org/abs/2603.24631" rel="noopener noreferrer"&gt;Coherence Collapse&lt;/a&gt;). Competence was not the missing ingredient. Everything around it was.&lt;/p&gt;

&lt;p&gt;Think about the sharpest graduate you ever hired. Now imagine handing them production credentials on day one: no induction, no runbook, nobody reviewing the work, and “done” taken at face value. You would not blame the graduate.&lt;/p&gt;





&lt;h2&gt;Key takeaways&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A framework gives you construction materials. A harness is what you built with them: real credentials, real limits, and a real definition of done.&lt;/li&gt;



&lt;li&gt;Two jobs never become the model’s, however good it gets. Holding the credential is one. Confirming that the work actually happened is the other.&lt;/li&gt;



&lt;li&gt;Prompt-level defenses against injection move the numbers, then adaptive attacks move them back. Enforcement outside the model is what holds.&lt;/li&gt;



&lt;li&gt;Grade every part of your harness on three questions: is there an owner, is there a mechanism, is there a failing test. A row with three no answers is a production risk to investigate first.&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;What is an agent harness?&lt;/h2&gt;

&lt;p&gt;An agent harness is the configured layer around a model that decides what it sees, what it may do, where those actions land, what survives a restart, and what counts as proof the work is finished.&lt;/p&gt;

&lt;p&gt;That is a working convention rather than a settled standard. The closest formal treatment is a 2026 preprint proposing harness engineering as the runtime substrate between a model and its environment (&lt;a href="https://arxiv.org/abs/2605.13357" rel="noopener noreferrer"&gt;AI Harness Engineering&lt;/a&gt;), demonstrated on one narrow software-engineering task. Useful rather than authoritative.&lt;/p&gt;

&lt;p&gt;Concretely, on that refunds queue: read access to the ticket queue and the orders table and nothing else, one payments credential that can only issue refunds under £200, a hard stop at ten tool calls, a run record in the database that survives a restart, and a check that reads the payments ledger back before the ticket is allowed to close. That set of choices is the harness. None of it is the model.&lt;/p&gt;

&lt;p&gt;The model proposes, the harness authorises, the environment acts, and a verifier decides whether any of it counted. Your graduate’s induction.&lt;/p&gt;





&lt;h2&gt;Would a bigger model make all this unnecessary?&lt;/h2&gt;

&lt;p&gt;The obvious objection is that all of this is temporary. Models keep improving and context windows keep growing, so surely a big enough model makes the surrounding structure redundant, and today’s harness is tomorrow’s dead weight. It is a fair question, and the honest answer is: some of it, yes. Start with what has been measured, because the same model scores very differently depending on the wrapper.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F09%2Fimage-2-1024x508.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F09%2Fimage-2-1024x508.png" alt="A bar chart of four task-success scores. A shell-only agent on GPT-4 Turbo reaches 11.0 per cent and the same model with a purpose-built code interface reaches 18.0 per cent. A minimal adapter on the GLM 5.1 backbone reaches 19.1 per cent and a full adapter on the same backbone reaches 73.4 per cent." width="800" height="397"&gt;&lt;/a&gt;Bar charts show higher task success when the model is surrounded by a purpose-built interface or full adapter.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Each pair holds the model fixed and changes only what surrounds it. In a peer-reviewed ablation over 300 SWE-bench Lite tasks, GPT-4 Turbo solved 18.0 per cent with a purpose-built interface to the codebase and 11.0 per cent driving a plain shell (&lt;a href="https://proceedings.neurips.cc/paper_files/paper/2024/file/5a7c947568c1b1328ccc5230172e1e7c-Paper-Conference.pdf" rel="noopener noreferrer"&gt;SWE-agent&lt;/a&gt;). In a 2026 preprint, a minimal and a full adapter on the same GLM 5.1 backbone scored 19.1 and 73.4 per cent (&lt;a href="https://arxiv.org/abs/2606.12344" rel="noopener noreferrer"&gt;Claw-SWE-Bench&lt;/a&gt;). No weights changed, and the score did. A third study, over 5,194 trajectories on 106 sandboxed tasks at shared budgets, found a 23.8-point gap between its best and worst configurable harnesses (&lt;a href="https://arxiv.org/abs/2605.27922" rel="noopener noreferrer"&gt;Harness-Bench&lt;/a&gt;). Each bundles several mechanisms per adapter, so none says which part did the work. Together they establish that the wrapper is a variable, not a detail.&lt;/p&gt;

&lt;p&gt;The counter-evidence is real. A long-context Gemini 2.5 Pro, given a fully observable environment and deliberately simple scaffolding, reached 50.8 per cent on SWE-bench Verified (&lt;a href="https://openreview.net/forum?id=IWvfQCkHRr" rel="noopener noreferrer"&gt;Putting It All into Context&lt;/a&gt;), and vendors report the same from the inside: context resets that stopped a weaker model finishing early became overhead on a stronger one (&lt;a href="https://www.anthropic.com/engineering/managed-agents" rel="noopener noreferrer"&gt;Scaling Managed Agents&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The split is the useful part. Scaffolding that compensates for weak reasoning should shrink as models improve. Controls that carry accountability should not: no gain in reasoning turns a model’s confidence into permission to touch the ledger.&lt;/p&gt;

&lt;p&gt;Two things never become the model’s job. The first is identity: a model holds no credential, it produces text that something else decides to act on, so “who is allowed to do this” has to be answered by a component with an identity of its own. A scoped, short-lived credential held by the process, not a role the model asserts about itself in a prompt. The second is the result. A model can report an outcome that never happened, because a plausible ending to a refund conversation is exactly what it was trained to produce. The forty-one unrefunded tickets were not a lie, they were writing. Neither failure is a reasoning error a larger model grows out of.&lt;/p&gt;





&lt;h2&gt;How is a harness different from a framework?&lt;/h2&gt;

&lt;p&gt;A framework gives you construction materials. A harness is what you built with them, bound to real credentials, real limits and a real definition of done. Teams conflate the two, and the cost is mistaking a successful pip install for production readiness.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;
&lt;td&gt;Layer&lt;/td&gt;
&lt;td&gt;What it is&lt;/td&gt;
&lt;td&gt;What it owns&lt;/td&gt;
&lt;td&gt;What it cannot decide&lt;/td&gt;
&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;The learned inference component that maps context to proposed actions (&lt;a href="https://proceedings.neurips.cc/paper_files/paper/2023/hash/d842425e4bf79ba039352da0f658a906-Abstract-Conference.html" rel="noopener noreferrer"&gt;Toolformer&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Semantic judgement, tool selection, interpretation&lt;/td&gt;
&lt;td&gt;Whether it holds the credential, or whether the call was allowed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;Reusable developer abstractions: graphs, messages, tool adapters, hooks, as in LangGraph, AutoGen and Semantic Kernel (&lt;a href="https://arxiv.org/abs/2508.10146" rel="noopener noreferrer"&gt;Agentic AI Frameworks&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Construction primitives and wiring&lt;/td&gt;
&lt;td&gt;The deployed application’s permissions, recovery rules or acceptance tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Harness&lt;/td&gt;
&lt;td&gt;The configured operational envelope binding a model to one task under real credentials, real limits and a real definition of done (&lt;a href="https://arxiv.org/abs/2605.13357" rel="noopener noreferrer"&gt;AI Harness Engineering&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Authority, containment, continuity, proof&lt;/td&gt;
&lt;td&gt;What the model is semantically capable of&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;The machinery that instantiates and advances that envelope for one run (&lt;a href="https://www.anthropic.com/engineering/managed-agents" rel="noopener noreferrer"&gt;Scaling Managed Agents&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Turn scheduling, dispatch, limits, event records&lt;/td&gt;
&lt;td&gt;The policy it is enforcing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment&lt;/td&gt;
&lt;td&gt;The filesystem, browser, database, network and services where actions take effect (&lt;a href="https://arxiv.org/abs/2405.15793" rel="noopener noreferrer"&gt;SWE-agent&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Authoritative state&lt;/td&gt;
&lt;td&gt;Anything about intent&lt;br&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F09%2FPicture3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F09%2FPicture3.png" alt="Five layers side by side. The model, framework, harness, runtime and environment each own something, and authority is decided in one of them: the framework supplies materials, the harness supplies the configured rules, and enforcement is then spread across IAM, the tool servers, database permissions and the runtime that enacts those rules against a real environment." width="800" height="286"&gt;&lt;/a&gt;Five-layer architecture diagram highlighting the harness as the layer that decides authority.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The comparison that matters most in a design review is the harness row against the framework row, and Oracle’s own LangChain work shows the line clearly. langchain-oracledb connects 26ai to LangChain as a vector store, and langgraph-oracledb gives a graph an OracleSaver for checkpoints and an OracleStore for cross-thread memory, so run state lands in a database with backup, auditing and access control rather than a file nobody is watching (&lt;a href="https://github.com/oracle/langchain-oracle" rel="noopener noreferrer"&gt;langchain-oracle&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That is worth having, and it is still framework. The integration hands the agent capabilities: it can reach the vectors, it can persist a checkpoint. What it does not hand anyone is permission or proof. Nothing in it decides whether this particular update was authorised, whether a half-finished external write can be retried, or what counts as evidence the task is done. Capabilities come from the framework. Permission and proof are configuration, and the configuration is the harness.&lt;/p&gt;





&lt;h2&gt;Which failure does each part of the harness absorb?&lt;/h2&gt;

&lt;p&gt;Read this table against your own system. Each row is a part, what it is for, and the failure it catches. If a row has no owner in your architecture, that failure is landing in production now.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;
&lt;td&gt;Part&lt;/td&gt;
&lt;td&gt;What it is for&lt;/td&gt;
&lt;td&gt;The failure it absorbs&lt;/td&gt;
&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Instructions and context&lt;/td&gt;
&lt;td&gt;Ranking authority: which text governs, and which is merely data the agent read (&lt;a href="https://proceedings.neurips.cc/paper_files/paper/2024/hash/97091a5177d8dc64b1da8bf3e1f6fb54-Abstract-Datasets_and_Benchmarks_Track.html" rel="noopener noreferrer"&gt;AgentDojo&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Retrieved text acquiring authority because it happened to arrive later in the window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tools&lt;/td&gt;
&lt;td&gt;Typed schemas, argument validation and call-time policy at the point uncertain judgement crosses into consequence (&lt;a href="https://modelcontextprotocol.io/docs/learn/architecture" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;A well-formed call the caller was never authorised to make&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment and permissions&lt;/td&gt;
&lt;td&gt;Isolation, scoped identity and short-lived credentials, so containment survives model error (&lt;a href="https://www.nccoe.nist.gov/sites/default/files/2026-02/accelerating-the-adoption-of-software-and-ai-agent-identity-and-authorization-concept-paper.pdf" rel="noopener noreferrer"&gt;NIST agent identity concept paper&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Ambient authority, where any instruction reaching the model activates every credential in the process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime state&lt;/td&gt;
&lt;td&gt;Durable task status, checkpoints, approvals and external-operation identifiers outside the context window (&lt;a href="https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents" rel="noopener noreferrer"&gt;Effective Harnesses for Long-Running Agents&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Losing work to a restart, or mistaking unfinished state for completion after compaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orchestration&lt;/td&gt;
&lt;td&gt;Legal transitions, retry budgets and deterministic gates before execution (&lt;a href="https://arxiv.org/abs/2607.07405" rel="noopener noreferrer"&gt;Reason Less, Verify More&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Repetition standing in for diagnosis, and loops that never terminate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verification&lt;/td&gt;
&lt;td&gt;Reading authoritative state back and grading it against the task contract, which is why τ-bench scores final database state and reports retail reliability below 25 per cent at pass^8 (&lt;a href="https://arxiv.org/abs/2406.12045" rel="noopener noreferrer"&gt;τ-bench&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;The refunds agent above: a completion claim with nothing behind it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three rows have unusually clean evidence, so cash them out.&lt;/p&gt;

&lt;p&gt;Orchestration first. Researchers put a deterministic gate before each action: ordinary code that reads the proposed call and refuses it if it breaks policy, no model involved. On the policy-permissive airline domain of τ²-bench, success rose from 29.6 to 42.0 per cent; where the tools already enforced policy it changed almost nothing (&lt;a href="https://arxiv.org/abs/2607.07405" rel="noopener noreferrer"&gt;Reason Less, Verify More&lt;/a&gt;). The gate pays where nothing else was checking.&lt;/p&gt;

&lt;p&gt;Verification carries the bleakest number here. Across 105 realistic multi-file coding tasks, the strongest pairing tested produced work both correct and secure 15.2 per cent of the time, and explicit security instructions did not significantly close the gap (&lt;a href="https://openreview.net/forum?id=8uDFRItIoe" rel="noopener noreferrer"&gt;SecureAgentBench&lt;/a&gt;). Telling the graduate to be careful is not reviewing their pull request.&lt;/p&gt;

&lt;p&gt;Then resources, which almost nobody counts as part of the harness. Varying only CPU and memory on Terminal-Bench 2.0, with model, harness and tasks held constant, moved scores by six points and dropped infrastructure failures from 5.8 to 0.5 per cent (&lt;a href="https://www.anthropic.com/engineering/infrastructure-noise" rel="noopener noreferrer"&gt;Quantifying Infrastructure Noise&lt;/a&gt;). Your resource ceilings are part of your agent’s measured capability. That makes them a harness setting rather than a separate infrastructure ticket. A memory ceiling is a configuration choice that changes what the agent can finish, exactly like a retry budget or a tool timeout, and when it bites, the failure arrives dressed as a reasoning error: a process killed mid-edit reads identically to an agent that gave up. Set it in the same review where you set the limits, and hold it constant whenever you ablate anything else.&lt;/p&gt;





&lt;h2&gt;What does a harness look like in real Oracle code?&lt;/h2&gt;

&lt;p&gt;It looks like a repository you can clone, not a diagram. The &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/main/workshops/enterprise-data-agent-harness-workshop" rel="noopener noreferrer"&gt;Enterprise Data Agent Harness workshop&lt;/a&gt; builds a memory-aware enterprise data agent one layer at a time, around an orchestration loop of some 300 lines of Python. Almost everything else is database primitives.&lt;/p&gt;

&lt;p&gt;The tool surface is the SQLcl MCP Server. MCP is the Model Context Protocol, the open standard for how an agent’s host application connects to servers that expose tools, resources and prompts rather than the model reaching data directly, and Oracle’s implementations sit at &lt;a href="https://oracle.com/mcp" rel="noopener noreferrer"&gt;oracle.com/mcp&lt;/a&gt;. The scratchpad is DBFS, the Database File System, which lets the database present ordinary files and directories: the agent’s working files then live under the same access control, backup and audit as the data itself. Oracle Data Access Controls scope what each user’s agent can reach.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/main/apps/oracle-data-migration-harness" rel="noopener noreferrer"&gt;oracle-data-migration-harness&lt;/a&gt; app lands on a problem customers actually turn up with: getting data out of somebody else’s platform without breaking what was built on it. It moves a RAG corpus from MongoDB into Oracle AI Database 26ai so the vectors keep working and JSON Relational Duality opens up SQL aggregation.&lt;/p&gt;

&lt;p&gt;What makes it a harness rather than a migration script is where the Oracle expertise lives. Not in a system prompt, but in a set of Oracle’s own published migration skills the agent retrieves and composes per job: sample, translate the schema, transfer, verify, reconcile. Move that into the prompt and you have deleted a layer.&lt;/p&gt;

&lt;p&gt;Underneath both, 26ai holds dense and sparse vectors beside relational and JSON data (&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/odpnt/featAIVectorSearch.html" rel="noopener noreferrer"&gt;AI Vector Search&lt;/a&gt;), row-level predicates through Virtual Private Database (&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/DBMS_RLS.html" rel="noopener noreferrer"&gt;DBMS_RLS&lt;/a&gt;) and unified auditing (&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg/introduction-to-auditing.html" rel="noopener noreferrer"&gt;auditing&lt;/a&gt;). Oracle AI Agent Memory installs with pip install oracleagentmemory (&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/agent_memory/oracle_agent_memory_developer_guide.ipynb" rel="noopener noreferrer"&gt;notebook&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Both of those, and a good deal more, live in the &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub" rel="noopener noreferrer"&gt;oracle-ai-developer-hub&lt;/a&gt; repository. If you want harness code rather than harness prose, start there.&lt;/p&gt;





&lt;h2&gt;What holds when the prompt does not?&lt;/h2&gt;

&lt;p&gt;Some of the text your agent reads was written by somebody who wants it to do something else: an email it is triaging, a comment in the code it is fixing, a page it fetched. That is prompt injection: instructions arriving through a channel you were treating as data. Prompt-level defenses reduce it measurably. What they do not hand you is a number that stays still once the attacker adapts.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F09%2Fimage-1-1024x508.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F09%2Fimage-1-1024x508.png" alt="A bar chart of attack success rates. An undefended ReAct-prompted GPT-4 agent follows indirect attacks 24 per cent of the time. A hardened browser stack under a fixed adaptive evaluation sits near 1 per cent. When attacks are re-optimised against a deployed filter, success recovers to 28 per cent overall and 64 per cent on tasks where fetched content shapes the action." width="800" height="397"&gt;&lt;/a&gt;Chart showing how attack success can increase when attackers adapt to existing defences.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Undefended, a ReAct-prompted GPT-4 followed indirect attacks in 24 per cent of 1,054 cases (&lt;a href="https://arxiv.org/abs/2403.02691" rel="noopener noreferrer"&gt;InjecAgent&lt;/a&gt;). Hardening moves that a long way, through instruction-hierarchy training (&lt;a href="https://openai.com/index/instruction-hierarchy-challenge/" rel="noopener noreferrer"&gt;Improving Instruction Hierarchy in Frontier LLMs&lt;/a&gt;) and, in one browser stack combining training, classifiers and red-teaming, down to roughly 1 per cent residual success at 100 attempts per environment, which the vendor still calls meaningful (&lt;a href="https://www.anthropic.com/research/prompt-injection-defenses" rel="noopener noreferrer"&gt;Mitigating the Risk of Prompt Injections in Browser Use&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Then the ground moves. AutoDojo re-optimised its attacks against a filter that had scored zero under static testing and recovered 28 per cent success, rising to 64 per cent where fetched content helped decide the action (&lt;a href="https://arxiv.org/abs/2606.15057" rel="noopener noreferrer"&gt;AutoDojo&lt;/a&gt;). A defence measured once is not a defence measured under pressure.&lt;/p&gt;

&lt;p&gt;The control that is least dependent on wording is enforcement outside the model. CaMeL derives control flow from the trusted user request, treats retrieved material as data, and attaches capabilities to values before any tool runs. It completed 77 per cent of AgentDojo tasks, compared with 84 per cent for the undefended agent (&lt;a href="https://arxiv.org/abs/2503.18813" rel="noopener noreferrer"&gt;CaMeL&lt;/a&gt;): seven points of utility for a structural property rather than a better average. Scoping tools helps and is not sufficient: in 17 per cent of AgentDojo’s cases the tools the legitimate task needed were already enough for the attack (&lt;a href="https://proceedings.neurips.cc/paper_files/paper/2024/file/97091a5177d8dc64b1da8bf3e1f6fb54-Paper-Datasets_and_Benchmarks_Track.pdf" rel="noopener noreferrer"&gt;AgentDojo&lt;/a&gt;).&lt;/p&gt;





&lt;h2&gt;How do you grade your own harness?&lt;/h2&gt;

&lt;p&gt;Go back to the table of parts and ask three questions of every row.&lt;/p&gt;

&lt;p&gt;Is there an &lt;strong&gt;owner&lt;/strong&gt;: a named person or team accountable when that row fails, rather than a box on a diagram. Is there a &lt;strong&gt;mechanism&lt;/strong&gt;: something in the code or the platform that enforces the row, rather than a prompt that requests it. Is there a &lt;strong&gt;failing test&lt;/strong&gt;: something in your pipeline or monitoring that turns red when the mechanism stops working.&lt;/p&gt;

&lt;p&gt;Resist turning that into a score. No published work weights these rows against each other, so a number would carry an authority the evidence behind it does not have. The questions are diagnostic, not a benchmark, and what they are good for is finding rows where all three answers are no.&lt;/p&gt;

&lt;p&gt;If asking cold feels too abstract, work backwards from your last bad incident. The agent said it was done and it was not: that is verification, and the fix is reading authoritative state back rather than grading the narration. It ran a command nobody would have approved: that is environment and permissions, where a scoped identity does what prompt wording cannot. It lost three hours at a restart and resumed from a summary missing a constraint: that is runtime state, and a compacted summary is an aid to inference rather than a checkpoint.&lt;/p&gt;

&lt;p&gt;Then ask the question that costs money: which rows have no owner, no mechanism and no failing test? Treat those rows as the highest-priority production risks, not as roadmap gaps.&lt;/p&gt;





&lt;h2&gt;The final word: where this is going&lt;/h2&gt;

&lt;p&gt;Three things look reasonably safe to say, and one does not.&lt;/p&gt;

&lt;p&gt;Harness effects are becoming measurable. “Our wrapper is better” was folklore until recently; there are now benchmarks that hold the model fixed, equalise budgets and evaluators, and vary only the wrapper (&lt;a href="https://arxiv.org/abs/2605.27922" rel="noopener noreferrer"&gt;Harness-Bench&lt;/a&gt;, &lt;a href="https://arxiv.org/abs/2606.12344" rel="noopener noreferrer"&gt;Claw-SWE-Bench&lt;/a&gt;). Configuration is becoming something you report rather than something left out of the table.&lt;/p&gt;

&lt;p&gt;The compensating half of the scaffold will thin. The long-context result and the vendor account of resets becoming overhead both point that way (&lt;a href="https://openreview.net/forum?id=IWvfQCkHRr" rel="noopener noreferrer"&gt;Putting It All into Context&lt;/a&gt;, &lt;a href="https://www.anthropic.com/engineering/managed-agents" rel="noopener noreferrer"&gt;Scaling Managed Agents&lt;/a&gt;). Code written to help a weaker model cope has a shelf life, so label it that way while you write it.&lt;/p&gt;

&lt;p&gt;The accountability half is becoming an identity problem rather than a prompting one. NIST NCCoE has an open concept paper on identity and authorisation for AI agents (&lt;a href="https://www.nccoe.nist.gov/sites/default/files/2026-02/accelerating-the-adoption-of-software-and-ai-agent-identity-and-authorization-concept-paper.pdf" rel="noopener noreferrer"&gt;NIST NCCoE&lt;/a&gt;), and CaMeL shows enforcement that is structural rather than statistical (&lt;a href="https://arxiv.org/abs/2503.18813" rel="noopener noreferrer"&gt;CaMeL&lt;/a&gt;). One plausible direction is for scoped, short-lived agent identity to become platform infrastructure, similar to service accounts.&lt;/p&gt;

&lt;p&gt;What nobody can tell you yet is which part of your harness carries the weight: no published study varies every part independently across several models at equal budgets (&lt;a href="https://arxiv.org/abs/2605.27922" rel="noopener noreferrer"&gt;Harness-Bench&lt;/a&gt;). That ablation is yours.&lt;/p&gt;

&lt;p&gt;Your graduate got good because somebody scoped their access, wrote down what “finished” meant, and read the work before it shipped. Nobody made them smarter first.&lt;/p&gt;





&lt;h2&gt;FAQ&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is an agent harness the same thing as an agent framework?&lt;/strong&gt; No.&amp;nbsp;A framework supplies reusable abstractions such as graphs, messages and tool adapters (&lt;a href="https://arxiv.org/abs/2508.10146" rel="noopener noreferrer"&gt;Agentic AI Frameworks&lt;/a&gt;). A harness is the configured deployment: which credentials, which limits, which recovery rules, which acceptance tests. You can adopt a framework and still have no harness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the Model Context Protocol give me a harness?&lt;/strong&gt; It gives you an interoperability boundary. MCP standardises host, client and server roles for tools, resources and prompts, and deliberately leaves model use and context management to the host application (&lt;a href="https://modelcontextprotocol.io/docs/learn/architecture" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt;). Oracle’s MCP servers are listed at &lt;a href="https://oracle.com/mcp" rel="noopener noreferrer"&gt;oracle.com/mcp&lt;/a&gt;. None of them will decide whether a particular refund is justified by the current task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will better models make the harness unnecessary?&lt;/strong&gt; Partly. Compensatory scaffolding does become redundant, and there is evidence a long-context model with a fully observable environment can compete with scaffolded systems (&lt;a href="https://openreview.net/forum?id=IWvfQCkHRr" rel="noopener noreferrer"&gt;Putting It All into Context&lt;/a&gt;). Containment and independent verification are not compensations for weak reasoning, so they stay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I just tell the model not to follow instructions in tool output?&lt;/strong&gt; That reduces the rate rather than removing it. Instruction-hierarchy training measurably helps (&lt;a href="https://openai.com/index/instruction-hierarchy-challenge/" rel="noopener noreferrer"&gt;Improving Instruction Hierarchy in Frontier LLMs&lt;/a&gt;), and adaptive attacks still recovered 28 per cent success against a filter that scored zero on static tests (&lt;a href="https://arxiv.org/abs/2606.15057" rel="noopener noreferrer"&gt;AutoDojo&lt;/a&gt;). Enforcement outside the model is what holds regardless of how the attack is worded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does Oracle AI Database 26ai fit?&lt;/strong&gt; As the governed data, memory, tool and audit plane underneath a harness, not as the harness itself. Select AI Agent covers task context, the Autonomous AI Database MCP Server covers permissioned database operations, and Oracle AI Agent Memory covers durable memory and context across runs (&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-agent2.html" rel="noopener noreferrer"&gt;Select AI Agent&lt;/a&gt;). Run history and checkpoints are a different layer again: those belong to your harness and runtime, not to the memory store. Repository execution, sandboxing and trajectory verification stay yours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I know which harness component caused a regression?&lt;/strong&gt; Version the whole configuration and ablate one thing at a time. No published factorial study varies every part across multiple models at equalised budgets (&lt;a href="https://arxiv.org/abs/2605.27922" rel="noopener noreferrer"&gt;Harness-Bench&lt;/a&gt;), so you will be doing this on your own system rather than reading it off a leaderboard.&lt;/p&gt;

</description>
      <category>agentskills</category>
      <category>agents</category>
      <category>ai</category>
      <category>database</category>
    </item>
    <item>
      <title>We just launched a new course on how to Build Adaptive AI Agents</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Fri, 28 Aug 2026 11:47:19 +0000</pubDate>
      <link>https://dev.to/oracledevs/we-just-launched-a-new-course-on-how-to-build-adaptive-ai-agents-2pkm</link>
      <guid>https://dev.to/oracledevs/we-just-launched-a-new-course-on-how-to-build-adaptive-ai-agents-2pkm</guid>
      <description>&lt;p&gt;Our new short course with DeepLearning.AI,&amp;nbsp;&lt;a href="https://www.deeplearning.ai/courses/building-adaptive-ai-agents" rel="noreferrer noopener"&gt;Building Adaptive AI Agents&lt;/a&gt;, is live today. It covers continual and adaptive learning: the&amp;nbsp;different ways&amp;nbsp;in which you can make an agent better over time, and how to do it on &lt;a href="https://www.oracle.com/database/" rel="noopener noreferrer"&gt;Oracle AI Database 26ai.&lt;/a&gt;&lt;/p&gt;





&lt;h2&gt;Key Takeaways&amp;nbsp;&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Agents forget because what one session works out rarely survives into the next: some setups carry a little project context, almost none carry the reasoning behind it.&lt;/li&gt;



&lt;li&gt;The cheapest fixes live in the&amp;nbsp;&lt;strong&gt;token&lt;/strong&gt;&amp;nbsp;space, turning yesterday's traces into reusable skills and restructuring memory, with zero training.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Structure beats keywords: we also explore Code Knowledge Graphs, a way to let agents find the correct context faster than simple regular expression matching.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Weight-space adaptation (retraining or finetuning) is the expensive tier you reach for only when context injection, processing and manipulation stops generalizing.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Best practice is an effort ladder: start&amp;nbsp;cheap, and&amp;nbsp;climb only when you must.&lt;/li&gt;
&lt;/ul&gt;





&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2F01-stateless-agent-throws-away-1024x373.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2F01-stateless-agent-throws-away-1024x373.png" alt="A stateless agent throws away the one thing worth keeping: Monday it builds&lt;br&gt;
a map of the codebase, nothing is written down, Tuesday it builds the same&lt;br&gt;
map again." width="799" height="291"&gt;&lt;/a&gt;A stateless AI agent rebuilds the same codebase map in a later session because it was never stored.&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;Why does your agent keep paying the same bill twice?&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;You ask your agent to add a field to the checkout flow. Before it writes a line, it greps for "checkout", opens six files to work out where the data layer&amp;nbsp;actually lives, and&amp;nbsp;reads through&amp;nbsp;your ORM setup. Twelve tool calls to build a map of a codebase that has not moved since Friday.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;It builds that map again on Tuesday. And on Wednesday.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;How much of last month's token spend went on your agent rediscovering things it already knew?&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The failure everyone braces for with coding agents is the wrong answer. That is not the one that costs the most. The one that costs the most is the right answer, worked out correctly, thrown away when the session closed, and then worked out again from scratch the next morning.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;Watch it happen on something smaller&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Your agent writes a query against your own schema and hits an error:&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ORA-00904: "CREATED_AT": invalid identifier&amp;nbsp;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So&amp;nbsp;it reads the schema, figures out the column it&amp;nbsp;actually needed&amp;nbsp;is&amp;nbsp;&lt;code&gt;DATE_CREATED&lt;/code&gt;, fixes the query and moves on. Fine. That is the agent doing its job.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Then the next day it is queried&amp;nbsp;again, and&amp;nbsp;starts a fresh session with a blank context window. Nothing reminds the new conversation that&amp;nbsp;&lt;code&gt;CREATED_AT&lt;/code&gt;&amp;nbsp;was wrong yesterday, so it figures it out all over again. Identical failure, identical token&amp;nbsp;spend, and nothing learned, so&amp;nbsp;you're&amp;nbsp;essentially wasting&amp;nbsp;tokens and time on unnecessary tool calls, thinking budget, context window buildup, etc.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;There is usually an absence of a feedback loop. The agent&amp;nbsp;&lt;em&gt;learned&lt;/em&gt;&amp;nbsp;something valuable yesterday and then threw it away with the context window after it was done.&amp;nbsp;&lt;strong&gt;Continual learning&lt;/strong&gt;&amp;nbsp;is the discipline of making an agent better over time, and it runs all the way from reusing yesterday's traces up to retraining the model itself. This piece, and most of the available wins, sit at the cheap end of that range: feeding the traces back so the agent stops paying for the same work twice.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/jasperan/" rel="noreferrer noopener"&gt;Nacho&lt;/a&gt;&amp;nbsp;spent the past couple of months watching agents fail this&amp;nbsp;way, and&amp;nbsp;looking at how frontier labs fix it: finetuning models, building better agent harnesses, fresh training runs. All of it assumes compute most of us do not have, and all of it reaches for the most expensive item on the list to fix something that was rarely broken there in the first place.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;So&amp;nbsp;the question that started this course was a smaller one.&amp;nbsp;&lt;em&gt;"My agent is live and getting things wrong. What is the cheapest change that makes it better, and how do I do it?"&lt;/em&gt;&amp;nbsp;Put another way: is there anything we can do with the data we already have?&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;Which kind of memory is your agent&amp;nbsp;actually missing?&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Every autonomous agent runs the same loop: perceive, decide, act,&amp;nbsp;observe, repeat. Each turn through that loop leaves traces, and those traces land in one of four memory forms:&amp;nbsp;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Memory form&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;What it stores&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;AI agent example&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Working&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;What the agent is thinking about right now&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Scratchpad, AI agent notes, TODOs, etc.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Episodic&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Specific past events&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Previous&amp;nbsp;agent conversations, errors that happened, etc.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Semantic&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Stable facts&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Domain knowledge about the company,&amp;nbsp;private data, private database schemas, etc.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Procedural&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;How to do things&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Skills, skill chains, workflows, notes to self, etc.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The four forms decay at different speeds, and they have different degrees of permanence: working memory evaporates, episodic memory (usually) fades, semantic facts last years, and procedural skills are almost set in stone.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2F02-four-kinds-of-memory-1024x387.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2F02-four-kinds-of-memory-1024x387.png" alt="Four kinds of memory ordered by how long they last: working memory goes with&lt;br&gt;
the turn, episodic fades, semantic lasts years, procedural is almost set in&lt;br&gt;
stone." width="799" height="302"&gt;&lt;/a&gt;Four types of AI agent memory—working, episodic, semantic, and procedural—shown by how long they persist.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;What your agent has at the end of Monday is episodic: the session where it grepped, opened six&amp;nbsp;files&amp;nbsp;and worked out where the data layer lives. What&amp;nbsp;it&amp;nbsp;needs on Tuesday&amp;nbsp;is&amp;nbsp;two other things. The map itself, which barely changes, is a semantic fact. How to find the next one is a procedural skill. Nothing in a default setup turns the episode into either, so it stays an episode and dies with the context window. Which form your agent is missing decides where in the course you should start.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;We built&amp;nbsp;&lt;a href="https://www.deeplearning.ai/courses/building-adaptive-ai-agents" rel="noreferrer noopener"&gt;Building Adaptive AI Agents&lt;/a&gt;&amp;nbsp;with Andrew Ng and the team at DeepLearning.AI.&amp;nbsp;&lt;a href="https://www.linkedin.com/in/jasperan/" rel="noreferrer noopener"&gt;Nacho Martinez&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="https://www.linkedin.com/in/casiusslee/" rel="noreferrer noopener"&gt;Casius Lee&lt;/a&gt;&amp;nbsp;wrote it on the Oracle Developers side, and we are both genuinely happy about how this partnership came together. It goes through all four memory forms with the code: skill extraction from real agent traces, a Code Knowledge Graph built and queried, and&amp;nbsp;LoRA&amp;nbsp;adapters for the point where context injection stops generalizing.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;It is live today. Your agent left the traces this morning and they are still sitting there.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.deeplearning.ai/courses/building-adaptive-ai-agents" rel="noreferrer noopener"&gt;Take the course on DeepLearning.AI&lt;/a&gt;&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;Frequently Asked Questions&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is continual learning?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Continual learning is the practice of feeding what an agent worked out in one session back into the next one, so it stops rediscovering things it already knew. It covers everything from turning yesterday's traces into reusable skills, through restructuring how memory is retrieved, up to retraining the model itself.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the token layer vs the weight layer?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The token layer is everything you can change without touching the model: what goes into the context window, which skills and memories get retrieved, and how they are structured. The weight layer means changing the model itself, through retraining or finetuning. Token-layer changes are cheap, take effect on the next run, and are where most of the available wins are. Weight-layer changes are expensive, and they are for the case where no amount of context injection produces the&amp;nbsp;behavior&amp;nbsp;reliably.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When do I need structure-aware retrieval?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;When the right memory exists but retrieval misses it: especially multi-hop questions where the answer lives two edges away from the query. Build the graph, seed anchors, and let a PageRank walk find what keyword search&amp;nbsp;can't.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When do I&amp;nbsp;actually fine-tune&amp;nbsp;the embedder?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;When retrieval fails because the embedder&amp;nbsp;doesn't&amp;nbsp;understand your domain's semantics: an agent needs "the code it will use next," not "the code that looks similar." Fine-tune on graded retrieval traces.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When do I need to fine-tune the weights?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;When the fix must generalize across a whole class of task and no context injection reliably produces it: removing a default&amp;nbsp;behavior, installing a persona. In the course we explore the use of quantization and&amp;nbsp;LoRA&amp;nbsp;adapters, that can allow cheap finetuning.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's&amp;nbsp;the cheapest first step?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Analyze&amp;nbsp;the traces you already have as&amp;nbsp;you've&amp;nbsp;been using your agents (Claude, Codex, etc.).&amp;nbsp;We'll&amp;nbsp;teach you how to make use of those traces your agent already leaves behind.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;Resources&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;• &lt;strong&gt;Course:&lt;/strong&gt;&amp;nbsp;&lt;a href="https://www.deeplearning.ai/courses/building-adaptive-ai-agents" rel="noreferrer noopener"&gt;Building Adaptive AI Agents&lt;/a&gt;, a DeepLearning.AI short course built with Oracle, live 26 August 2026&amp;nbsp;&lt;/p&gt;

&lt;p&gt;• &lt;strong&gt;Agent memory:&lt;/strong&gt;&amp;nbsp;&lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/agmea/" rel="noreferrer noopener"&gt;Oracle AI Agent Memory documentation&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="https://pypi.org/project/oracleagentmemory/" rel="noreferrer noopener"&gt;oracleagentmemory&lt;/a&gt;&amp;nbsp;26.6.0&amp;nbsp;&lt;/p&gt;

&lt;p&gt;• &lt;strong&gt;Database:&lt;/strong&gt;&amp;nbsp;&lt;a href="https://www.oracle.com/database/" rel="noreferrer noopener"&gt;Oracle AI Database 26ai&lt;/a&gt;: HNSW vector indexes,&amp;nbsp;VECTOR_DISTANCE, and property-graph (PGQ) traversal&amp;nbsp;&lt;/p&gt;

</description>
      <category>deeplearning</category>
      <category>agentskills</category>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Build a Controlled Cursor MCP Workflow with Oracle AI Database</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Fri, 28 Aug 2026 11:44:49 +0000</pubDate>
      <link>https://dev.to/oracledevs/build-a-controlled-cursor-mcp-workflow-with-oracle-ai-database-474n</link>
      <guid>https://dev.to/oracledevs/build-a-controlled-cursor-mcp-workflow-with-oracle-ai-database-474n</guid>
      <description>&lt;p&gt;&lt;strong&gt;Giving an AI assistant database access is easy. Making&amp;nbsp;that access&amp;nbsp;controlled, inspectable, and repeatable is the hard part. Here is the Cursor CLI and Oracle AI Database workflow that does it.&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Companion notebook: &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/cursor_mcp_oracle_ai_database.ipynb" rel="noopener noreferrer"&gt;Cursor MCP with Oracle AI Database Workflow&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Key Takeaways&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://modelcontextprotocol.io/docs/2026-07-28/getting-started/intro" rel="noreferrer noopener"&gt;MCP&lt;/a&gt;&amp;nbsp;turns AI-to-database access into an explicit tool contract instead of implicit system access.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.2/sqcug/sqlcl-mcp-server.html" rel="noreferrer noopener"&gt;Oracle SQLcl in MCP&lt;/a&gt;&amp;nbsp;mode,&amp;nbsp;sql&amp;nbsp;-mcp, is a practical way to connect Cursor CLI to Oracle AI Database through a local MCP server.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Oracle AI Database provides the persistent storage and vector search layer for memory workloads, while&amp;nbsp;&lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.6/guide/api/agentmemory.html" rel="noreferrer noopener"&gt;Oracle AI Agent Memory&lt;/a&gt;&amp;nbsp;gives teams a Python API for threads, durable memories, scoped retrieval, and context assembly on top of it.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.langchain.com/oss/python/integrations/vectorstores/oracle" rel="noreferrer noopener"&gt;LangChain&lt;/a&gt;&amp;nbsp;can provide application-side wrappers and orchestration after the Oracle-backed memory and retrieval path is in place.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;A strong default is hybrid: Cursor CLI plus MCP for interactive database work, Oracle AI Database plus Oracle AI Agent Memory for durable memory, and&amp;nbsp;LangChain&amp;nbsp;only when the application needs reusable retrieval orchestration.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;





&lt;p&gt;Here is how those components connect in this pattern. The important visual point is that Cursor does not connect directly to Oracle AI Database. Cursor calls&amp;nbsp;SQLcl&amp;nbsp;MCP tools,&amp;nbsp;SQLcl&amp;nbsp;uses a saved Oracle connection, and Oracle AI Database&amp;nbsp;remains&amp;nbsp;the durable store for memory records, retrieval evidence, vectors, and tool traces. Oracle AI Agent Memory and&amp;nbsp;LangChain&amp;nbsp;sit in the application layer after that database-backed path is in place.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Production success depends less on clever prompting and more on boundaries, privileges, logging, scoped retrieval, and repeatable runbooks.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;This guide is for developers who want Cursor CLI to work with Oracle AI Database through explicit tools, durable memory, and reviewable retrieval evidence.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The developer path through this guide is simple:&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;ol start="1"&gt;
&lt;li&gt;Start with&amp;nbsp;one&amp;nbsp;approved Oracle connection and a read-only validation query.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;

&lt;ol start="2"&gt;
&lt;li&gt;Put&amp;nbsp;SQLcl&amp;nbsp;MCP in front of that&amp;nbsp;connection&amp;nbsp;so Cursor sees tools, not raw database credentials.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;

&lt;ol start="3"&gt;
&lt;li&gt;Check the audit and activity trail before adding more tool access.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;

&lt;ol start="4"&gt;
&lt;li&gt;Add Oracle AI Agent Memory when the workflow needs durable thread context, scoped recall, or reusable context cards.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;

&lt;ol start="5"&gt;
&lt;li&gt;Add&amp;nbsp;&lt;a href="https://docs.langchain.com/oss/python/integrations/vectorstores/oracle" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt;&amp;nbsp;only when you need application-side&amp;nbsp;retrieval&amp;nbsp;orchestration beyond the MCP interaction loop.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FDiagram-2-1024x391.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FDiagram-2-1024x391.png" alt="Architecture diagram showing a human operator using Cursor CLI and SQLcl MCP Server to access Oracle AI Database and Oracle AI Agent Memory, with memory tables, tool logs, vector retrieval, LangChain retrieval, and grounded context." width="799" height="305"&gt;&lt;/a&gt;Controlled Cursor MCP + Oracle AI Database Workflow&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Why This Architecture Is Useful for Developers&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Giving an AI assistant database access is easy. Making&amp;nbsp;that access&amp;nbsp;controlled, inspectable, and repeatable is the hard part.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Cursor is useful because it sits close to the developer's actual work: code, terminal commands, notebooks, configuration, and implementation details. A developer can move from "why is this failing?" to "inspect the database state" inside the same working loop. That closeness is powerful, but it also makes the&amp;nbsp;database&amp;nbsp;boundary more sensitive.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The question is not whether Cursor can produce SQL-shaped text. The question is whether the database path is approved, observable, and easy to debug later. A useful workflow needs to preserve the request, the tool call, the database identity, the retrieved context, and the reason a risky action was allowed or blocked.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;By the end of this guide, you should know how to connect Cursor CLI to Oracle AI Database through a controlled MCP boundary, when Cursor context is enough and when Oracle-backed memory is needed, and how to build a retrieval path that can be queried, audited, and scaled.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The companion notebook is intentionally practical. It validates&amp;nbsp;SQLcl&amp;nbsp;and Java discovery, writes a sanitized Cursor MCP config preview, checks the saved&amp;nbsp;SQLcl&amp;nbsp;connection alias, creates memory tables, inserts simulated Cursor/MCP teaching traces, tests lexical, vector, and hybrid retrieval, initializes Oracle AI Agent Memory with the current 26.6 configuration shape, and finishes with a validation snapshot.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The workflow has four layers. Cursor CLI is the developer-facing agent interface.&amp;nbsp;SQLcl&amp;nbsp;MCP is the tool boundary. Oracle AI Database is the durable substrate for memory, traces, and retrieval. The notebook is the build-and-validation harness that proves the pieces are wired correctly before the workflow is handed to Cursor.&amp;nbsp;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Layer&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;
&lt;span&gt;&lt;span&gt;Responsibility&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor CLI&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Developer-facing MCP client and agent interface.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLcl&amp;nbsp;MCP&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Exposes declared Oracle tools to Cursor; it is the tool boundary.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle AI Database&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Stores durable data, retrieval evidence, vectors, metadata, traces, and enforces database privileges.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle AI Agent Memory&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Provides application APIs for users, agents, threads, durable memories, scoped retrieval, and context assembly.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LangChain&amp;nbsp;&lt;/td&gt;
&lt;td&gt;
&lt;span&gt;&amp;nbsp;&lt;/span&gt;Wraps Oracle-backed retrieval results as Document objects and supports application-side orchestration.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;





&lt;h2&gt;
&lt;strong&gt;The Two Execution Loops&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Building on that split between tool boundary and durable store, the system naturally forms two execution loops:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Loop A &lt;/strong&gt;is the operational interaction loop: Cursor CLI works with MCP to discover tools, inspect data, run bounded read-only queries, and return results&amp;nbsp;immediately.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Loop B&lt;/strong&gt; is the durable memory and retrieval loop: application code writes history, tool logs, memory records, chunks, and embeddings to Oracle AI Database, then retrieves context before a later answer or workflow step.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FDiagram-1-1024x415.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FDiagram-1-1024x415.png" alt="Diagram showing User to Cursor CLI to SQLcl MCP to Oracle AI Database to Answer, with a second loop where tool traces enter a memory store, Oracle AI Agent Memory, hybrid retrieval, and grounded context returned to Claude." width="799" height="324"&gt;&lt;/a&gt;Dual Execution Loop: MCP Interaction and Durable Memory&amp;nbsp;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;SQLcl&amp;nbsp;MCP handles live tool use. Oracle AI Agent Memory handles durable memory and scoped recall. Most production setups need both loops, but they solve different problems.&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Reproducing the&amp;nbsp;SQLcl&amp;nbsp;MCP and Cursor CLI Workflow&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;The setup should be reproducible.&amp;nbsp;SQLcl&amp;nbsp;runs in MCP mode with&amp;nbsp;&lt;code&gt;sql&amp;nbsp;-mcp&lt;/code&gt;. Cursor CLI launches it as an MCP server and talks to Oracle through declared tools, not through direct access. Connections come from saved&amp;nbsp;SQLcl&amp;nbsp;profiles that you create and test before Cursor uses them.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Cursor does not invent them at runtime; it reuses profiles you have already created and validated.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites before you connect Cursor CLI:&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oracle&amp;nbsp;SQLcl&amp;nbsp;25.2.0 or higher.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Oracle JRE 17 or 21.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Cursor IDE or Cursor CLI if you want to use the generated MCP configuration outside the notebook.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;At least one saved&amp;nbsp;SQLcl&amp;nbsp;connection profile under&amp;nbsp;&lt;code&gt;~/.dbtools&lt;/code&gt;, created with password persistence for MCP use.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;A database user with the minimum permissions&amp;nbsp;required&amp;nbsp;for the workflow.&amp;nbsp;Start with read-only access and a sanitized development or replica environment where possible.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The notebook deliberately treats the saved&amp;nbsp;SQLcl&amp;nbsp;connection alias as a first-class artifact. In local development, that alias is what lets&amp;nbsp;SQLcl&amp;nbsp;MCP connect without forcing the agent to assemble credentials dynamically.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The notebook then generates a sanitized Cursor MCP config preview.&amp;nbsp;The preview is intentionally safe: it shows the server command and arguments without exposing secrets.&amp;nbsp;It does not overwrite your real Cursor MCP configuration.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;For the saved connection itself, the important detail is&amp;nbsp;&lt;code&gt;-savepwd&lt;/code&gt;. MCP cannot stop and ask a human for the password every time Cursor invokes a database tool. The saved alias becomes the repeatable local path Cursor can use after you have reviewed it.&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{ 
  "mcpServers": { 
    "sqlcl": { 
      "command": "/absolute/path/to/sql", 
      "args": ["-mcp"] 
    } 
  } 
} &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That small JSON block defines the connection between Cursor and&amp;nbsp;SQLcl&amp;nbsp;MCP Server. Cursor interacts with the database through the tools and permissions exposed by the MCP server, using the saved&amp;nbsp;SQLcl&amp;nbsp;connection profile you created and tested first. Save this configuration in&amp;nbsp;.cursor/mcp.json&amp;nbsp;for a project-scoped setup or&amp;nbsp;~&lt;code&gt;/.cursor/mcp.json&lt;/code&gt;&amp;nbsp;globally, restart Cursor or Cursor CLI, then run:&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cursor-agent&amp;nbsp;mcp&amp;nbsp;list&amp;nbsp;
cursor-agent&amp;nbsp;mcp&amp;nbsp;list-tools&amp;nbsp;sqlcl&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A useful first prompt is intentionally constrained:&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Use&amp;nbsp;SQLcl&amp;nbsp;MCP to list available saved Oracle connections. Do not run DML or DDL.&amp;nbsp;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Validation checklist before expanding access:&amp;nbsp;Run&amp;nbsp;&lt;code&gt;sql&amp;nbsp;-mcp&lt;/code&gt;&amp;nbsp;locally and confirm the server starts.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restart Cursor CLI and confirm the&amp;nbsp;SQLcl&amp;nbsp;tools are discoverable.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Run one read-only query against an approved schema.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Check database-side MCP activity logs and session metadata.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Document the connection alias, database user, grant scope, restrict level, and troubleshooting owner.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good first proof looks like this:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The MCP server starts without a Java or path error.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Cursor lists the&amp;nbsp;SQLcl&amp;nbsp;MCP tools after&amp;nbsp;restart.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;A read-only query succeeds against the expected schema.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;The notebook audit trail records the expected tool interaction in&amp;nbsp;&lt;code&gt;cursor_tool_logs&lt;/code&gt;.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;For live Cursor CLI +&amp;nbsp;SQLcl&amp;nbsp;MCP validation, confirm the database/session activity through your normal Oracle monitoring path.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;A denied query fails because of the database role, not because a prompt asked nicely.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful MCP boundary is more than&amp;nbsp;tool&amp;nbsp;discovery. The notebook models read-only defaults, confirmation requirements, scope checks, and controlled failure examples so&amp;nbsp;denied&amp;nbsp;and warning states are visible.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read-only default: start with inspection and diagnostics before allowing changes.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Confirmation gate: require explicit approval for medium-risk, write-like, or destructive actions.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Scope control: keep user, tenant, and schema filters close to the database query.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Failure trace: store denied calls and warnings as evidence instead of hiding them.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
&lt;strong&gt;What a Successful Notebook Run Shows&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;The notebook is not just setup prose. It produces concrete checkpoints that make the workflow inspectable.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The first useful result is a deterministic Cursor/MCP timeline. The sample data uses explicit event sequence values and simulated event&amp;nbsp;timestamps&amp;nbsp;so the workflow order is stable every time the notebook is rerun:&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;step  event_kind    actor                              result 
1     CONVERSATION  user                               initial support-job request 
2     CONVERSATION  assistant                          SQLcl MCP read-only plan 
3     MCP_TOOL      sqlcl.connections/list_connections SUCCESS 
4     MCP_TOOL      sqlcl.sql/query                    SUCCESS 
5     MCP_TOOL      sqlcl.sql/query                    DENIED / PRIVILEGE_SCOPE 
6     CONVERSATION  assistant                          grounded summary&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That ordering matters because operational memory is only useful if the answer can be traced back to the request, the tool calls, and the permission boundary that shaped the result.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The grounding package also returns visible evidence before the assistant answer is assembled:&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Status: READY&amp;nbsp;

Top evidence:&amp;nbsp;

- Saved&amp;nbsp;SQLcl&amp;nbsp;connections for MCP&amp;nbsp;

-&amp;nbsp;SQLcl&amp;nbsp;MCP execution boundary&amp;nbsp;

- Tool logging baseline&amp;nbsp;

-&amp;nbsp;LangChain&amp;nbsp;as orchestration glue&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If retrieval is empty or too weak, the notebook returns&amp;nbsp;&lt;code&gt;INSUFFICIENT_CONTEXT&lt;/code&gt;&amp;nbsp;and displays a safe empty-result message instead of trying to select columns from a missing evidence table.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The final snapshot should show every local layer that is ready:&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Cursor MCP config&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; generated&amp;nbsp;
SQLcl&amp;nbsp;MCP runtime&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ready&amp;nbsp;
SQLcl&amp;nbsp;saved connection&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ready&amp;nbsp;
Oracle AI Database memory&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ready&amp;nbsp;
Oracle AI Agent Memory package&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ready&amp;nbsp;
Lexical search&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ready&amp;nbsp;
Native VECTOR execution path&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ready&amp;nbsp;
Demo embeddings&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; demo ready&amp;nbsp;
Hybrid retrieval&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ready&amp;nbsp;
LangChain&amp;nbsp;wrapper&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ready&amp;nbsp;
validation_action_needed&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That is the practical bar for this demo: Cursor setup artifacts are generated,&amp;nbsp;SQLcl&amp;nbsp;MCP prerequisites are&amp;nbsp;validated, Oracle memory tables are populated, retrieval works, Agent Memory initializes, and the notebook separates native VECTOR readiness from deterministic demo embeddings.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Simulated Teaching Data, Not Live Cursor Telemetry&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;One important boundary in the companion notebook is that the operational records are simulated teaching data. The notebook inserts sample conversation rows and sample tool-log&amp;nbsp;rows to show what a production workflow should preserve: the user's request, Cursor's plan, tool calls, outcomes, controlled failures, and retrieval evidence.&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Those rows are not live telemetry captured from Cursor CLI, and the notebook does not automatically&amp;nbsp;observe, scrape, or stream Cursor CLI activity. Live Cursor validation still happens through Cursor's MCP configuration and the&amp;nbsp;SQLcl&amp;nbsp;MCP server. The notebook proves the database-backed memory, retrieval, and validation pattern around that&amp;nbsp;workflow&amp;nbsp;so the pieces are inspectable and repeatable.&lt;/p&gt;





&lt;h2&gt;&lt;strong&gt;Why Put Application Memory Records in Oracle AI Database, Not Just Outputs&amp;nbsp;&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Once the first MCP tool calls work, the next challenge is continuity. This is where long-term memory for AI agents becomes different from&amp;nbsp;short-lived chat context.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;If memory lives only in chat context, the system is fragile. If memory is scattered across files without structure, retrieval and auditing become expensive over time.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;A database-backed memory model is usually cleaner and more scalable. The companion notebook builds this memory layer from&amp;nbsp;scratch&amp;nbsp;so the mechanics are visible, then shows how Oracle AI Agent Memory sits on top of it once the substrate is working.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Memory categories that matter in practice:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conversation memory keeps the important&amp;nbsp;user&amp;nbsp;and assistant&amp;nbsp;turns&amp;nbsp;that future sessions may need.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Operational memory keeps tool calls, outcomes, warnings, and failures so a team can debug what happened.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Semantic memory adds embeddings so the system can find relevant context even when the user asks in different words.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, hybrid retrieval for agent memory usually combines exact operational terms, such as&amp;nbsp;&lt;code&gt;sql&amp;nbsp;-mcp&lt;/code&gt;&amp;nbsp;or&amp;nbsp;&lt;code&gt;cursor_mc&lt;/code&gt;p, with semantic search over memory records.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The notebook shows the lower-level mechanics&amp;nbsp;first&amp;nbsp;so the storage and retrieval path is visible. This is also a context engineering problem: the application&amp;nbsp;has to&amp;nbsp;decide which memories, tool traces, and retrieval results should be assembled before Cursor or another assistant answers. Oracle AI Agent Memory then gives application code a cleaner&amp;nbsp;package&amp;nbsp;API over that same database-backed idea.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Where Oracle AI Agent Memory Fits&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Oracle AI Agent Memory sits between your application code and Oracle AI Database. The package manages conversation threads, durable memory records, scoped retrieval, and context assembly while Oracle AI Database&amp;nbsp;remains&amp;nbsp;the storage layer underneath.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The notebook includes an abbreviated package-backed memory pattern. It initializes&amp;nbsp;OracleAgentMemory&amp;nbsp;with a database connection pool and a custom local deterministic embedder.&amp;nbsp;&lt;code&gt;LocalCursorEmbedder&lt;/code&gt;&amp;nbsp;is notebook code, not a built-in Oracle AI Agent Memory embedder.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The local embedder is intentionally billing-free, which makes the notebook runnable for people who do not want to attach paid model usage to a tutorial.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;In Oracle AI Agent Memory 26.6, use&amp;nbsp;&lt;code&gt;MemoryExtractionConfig(extract_memories=False)&lt;/code&gt;&amp;nbsp;instead of the older &lt;code&gt;inline&amp;nbsp;extract_memories=False&lt;/code&gt;&amp;nbsp;parameter, and use&amp;nbsp;&lt;code&gt;memory_store_id&lt;/code&gt;&amp;nbsp;instead of&amp;nbsp;&lt;code&gt;table_name_prefix&lt;/code&gt;.&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from oracleagentmemory.apis.searchscope import SearchScope 
from oracleagentmemory.core import MemoryExtractionConfig 
from oracleagentmemory.core.oracleagentmemory import OracleAgentMemory 
  
db_pool = oracledb.SessionPool( 
    user=CONFIG["ORACLE_USER"], 
    password=CONFIG["ORACLE_PASSWORD"], 
    dsn=CONFIG["ORACLE_DSN"], 
    min=1, 
    max=4, 
    increment=1, 
) 
  
agent_memory = OracleAgentMemory( 
    connection=db_pool, 
    embedder=LocalCursorEmbedder(dimensions=32), 
    llm=None, 
    memory_extraction_config=MemoryExtractionConfig(extract_memories=False), 
    schema_policy="create_if_necessary", 
    memory_store_id="cursor_oam_local", 
) &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br&gt;Use oracleagentmemory from your application layer when you need package-managed users, agents, memories, threads, scoped retrieval, and context assembly. Keep systems of record separate from memory records: memory helps provide context, but application logic and authoritative data sources should still decide what is true, allowed, and final. &lt;br&gt;&lt;br&gt;Implementation note: use a schema whose default tablespace supports the JSON objects created by Agent Memory. If a local SYSTEM schema sits on a tablespace that rejects JSON object creation, the better production answer is a dedicated application schema. &lt;br&gt;&lt;br&gt;Production note: the package-managed expired-record purge job needs CREATE JOB or an equivalent scheduler-job privilege, or a managed schema setup flow. Without that privilege, expired messages and memories will not be purged automatically. For a local notebook demo, this is acceptable as long as the limitation is visible. &lt;/p&gt;





&lt;h2&gt;Store and Search: What a Realistic Memory Looks Like &lt;/h2&gt;

&lt;p&gt;A realistic Cursor memory is not&amp;nbsp;generic&amp;nbsp;trivia about a user. For this workflow, memory should capture how a developer actually works: the connection name they used, the&amp;nbsp;SQLcl&amp;nbsp;path that succeeded, the MCP config location, the failed privilege boundary, the retrieval query that helped, and the final fix that should be reused later.&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;thread = agent_memory.create_thread( 
    user_id=AGENT_MEMORY_USER_ID, 
    agent_id=AGENT_MEMORY_AGENT_ID, 
) 
  
thread.add_memory( 
    "Developer validated Cursor CLI with SQLcl MCP alias cursor_mcp " 
    "against local Oracle AI Database service FREEPDB1." 
) 
  
results = agent_memory.search( 
    query="Cursor SQLcl MCP alias validation and Agent Memory setup", 
    scope=SearchScope(user_id=AGENT_MEMORY_USER_ID, agent_id=AGENT_MEMORY_AGENT_ID), 
) &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That kind of memory pays off because it is operational. It can help Cursor answer the next question with context from the&amp;nbsp;previous&amp;nbsp;debugging session, but it is still scoped and retrievable through a database-backed API.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Vector Search, Native VECTOR, and Demo Embeddings&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Vector search is part of the Oracle AI Database memory story. In a real application, embeddings usually come from a model and are indexed with Oracle AI&amp;nbsp;Database&amp;nbsp;vector capabilities.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The notebook separates two ideas that are easy to accidentally blur:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native VECTOR readiness means the database can store and search vectors through the Oracle AI Database vector path.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Deterministic demo embeddings are local, repeatable vectors used so the notebook can run without external model billing.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The deterministic embeddings are useful for portability and inspection, but they should not be described as&amp;nbsp;productionsemantic embeddings. For production, replace the notebook's&amp;nbsp;&lt;code&gt;demo_embed()&lt;/code&gt;&amp;nbsp;or&amp;nbsp;&lt;code&gt;LocalCursorEmbedder&lt;/code&gt;&amp;nbsp;with a supported embedding model after cost, latency, privacy, and retrieval-quality review.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The final notebook snapshot makes this separation explicit with two rows:&amp;nbsp;Native VECTOR execution path&amp;nbsp;and&amp;nbsp;Demo embeddings.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FDiagram-3-1-386x1024.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FDiagram-3-1-386x1024.png" alt="Vertical flow from user query through tenant filter, lexical search using Oracle Text, vector search using Oracle VECTOR, hybrid scoring, grounding package, and final answer." width="386" height="1024"&gt;&lt;/a&gt;Hybrid search pipeline combining tenant filtering, Oracle Text lexical search, Oracle VECTOR search, scoring, and a grounding package.&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Where&amp;nbsp;&lt;a href="https://docs.langchain.com/oss/python/integrations/vectorstores/oracle" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt;&amp;nbsp;Adds Value&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;LangChain&amp;nbsp;should not be treated as the source of truth. Cursor CLI does not call&amp;nbsp;LangChain&amp;nbsp;directly in this architecture, and&amp;nbsp;LangChain&amp;nbsp;is not the permission boundary, memory store, or audit layer.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;In this notebook,&amp;nbsp;LangChain&amp;nbsp;is used as a compatibility layer. The custom Oracle-backed&amp;nbsp;&lt;code&gt;hybrid_search()&lt;/code&gt; path performs retrieval, then the results are wrapped as&amp;nbsp;LangChain&amp;nbsp;Document&amp;nbsp;objects&amp;nbsp;so applications that already expect&amp;nbsp;LangChain&amp;nbsp;interfaces can consume them.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;By the time&amp;nbsp;LangChain&amp;nbsp;is introduced, the database tables, package memory, retrieval scores, and validation&amp;nbsp;snapshot already exist.&amp;nbsp;LangChain&amp;nbsp;becomes a wrapper around evidence, not a substitute for evidence.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Use it when the consuming application already expects retrievers, documents, chains, or&amp;nbsp;tool&amp;nbsp;orchestration. If the application only needs direct SQL, package-backed Agent Memory search, or a simple evidence table, the extra abstraction can make debugging harder.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;How to Move This from Demo to Production&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;The difference between demo success and production success is disciplined operations.&amp;nbsp;In this workflow, the first failures to check are usually integration issues:&amp;nbsp;SQLcl&amp;nbsp;discovery, Java runtime, saved connection aliases, database permissions, and retrieval configuration.&amp;nbsp;A local notebook can prove the wiring. A production workflow needs smaller database roles, managed secrets, clear MCP approval policy, repeatable environment setup, and&amp;nbsp;monitoring aroundmemory writes and tool calls.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Access and privilege model:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a dedicated application schema where possible, then tighten grants with least-privilege roles and quotas.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Keep saved&amp;nbsp;SQLcl&amp;nbsp;aliases separate by role and environment instead of sharing one broad connection.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Start read-only wherever possible and&amp;nbsp;gate write&amp;nbsp;operations with explicit confirmation workflows.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Use schema allowlists and separate accounts for development,&amp;nbsp;test, and production.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Observability model:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log tool name, thread ID, timestamp, status, and sanitized inputs and outputs.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Classify failures into runtime, discovery, connection, permission, query, and retrieval categories.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Keep a troubleshooting playbook in the&amp;nbsp;repo&amp;nbsp;so setup issues do not become tribal knowledge.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Check whether&amp;nbsp;retrieval&amp;nbsp;quality changes as more data and memory records are added.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reliability model:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer deterministic SQL patterns with bounded result sets.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Use retrieval-first context assembly for memory-heavy tasks.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Avoid giant context stuffing as a substitute for memory design.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Review and prune tool surfaces periodically.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Move from the local deterministic embedder to a supported embedding model after cost, latency, and privacy review.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
&lt;strong&gt;What to Check When the Workflow Fails&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;When this workflow fails, start with the integration points below.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runtime failure:&amp;nbsp;sql&amp;nbsp;-mcp&amp;nbsp;does not start.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the absolute&amp;nbsp;SQLcl&amp;nbsp;path, confirm Java is available, and run&amp;nbsp;sql&amp;nbsp;-mcp&amp;nbsp;outside Cursor first. Resolve runtime issues before checking assistant behavior.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discovery failure: Cursor does not see tools.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the Cursor MCP configuration, confirm the configured command points to the&amp;nbsp;SQLcl&amp;nbsp;executable, and restart or reload Cursor CLI after edits.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connection failure: tools are&amp;nbsp;present&amp;nbsp;but queries fail&amp;nbsp;immediately.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the saved&amp;nbsp;SQLcl&amp;nbsp;connection alias, confirm the profile lives under the expected&amp;nbsp;SQLcl&amp;nbsp;connection store, and verify password persistence for the MCP workflow. Then test the same connection outside Cursor.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Permission failure: queries execute selectively and fail on specific objects.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the database role first. A selective failure can be the right outcome when&amp;nbsp;least&amp;nbsp;privilege is working. Add grants intentionally and keep read-write access separate from the&amp;nbsp;initial&amp;nbsp;validation path.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retrieval&amp;nbsp;quality failure: answers are fluent but weakly grounded.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inspect the retrieved records before blaming the model. Check chunk size, metadata filters, embedding choice, top-k settings, and whether the query is asking for exact history, semantic similarity, or operational logs. The&amp;nbsp;notebook's grounding package includes an&amp;nbsp;INSUFFICIENT_CONTEXT&amp;nbsp;path and&amp;nbsp;guards&amp;nbsp;empty evidence before displaying result columns.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Why the Hybrid Model Is Usually the Best Long-Term Design&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;No single layer handles both execution and memory well. Trying to force everything into Cursor context gets messy fast: you either lose control over execution, or you stuff too much state into prompts just to keep things working. On the other&amp;nbsp;side, if you only build backend memory systems, you lose the speed and usability that makes an assistant useful during development.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The hybrid approach works because it does not force one layer to do everything. MCP handles live tool execution, Oracle AI Database keeps durable evidence, Oracle AI Agent Memory provides the memory API, and&amp;nbsp;LangChain&amp;nbsp;is added only when the application needs that shape.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;In real&amp;nbsp;teams&amp;nbsp;this usually evolves over time. Start with Cursor CLI,&amp;nbsp;SQLcl&amp;nbsp;MCP, read-only access, and strong logging. Once people rely on the workflow, the gaps become visible: lost context, weak traceability, repeated setup work, or retrieval that is hard to explain. That is when database-backed memory and structured retrieval become worth adding.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;For Cursor specifically, the value is that the assistant can stay in the developer loop without becoming an unreviewed database actor.&amp;nbsp;Cursor&amp;nbsp;can help plan, inspect, and explain.&amp;nbsp;SQLcl&amp;nbsp;MCP exposes the database path as tools. Oracle AI Database keeps the durable evidence. That is the combination that makes the workflow useful after the demo.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;A Cursor and&amp;nbsp;SQLcl&amp;nbsp;MCP workflow&amp;nbsp;becomes&amp;nbsp;useful when it is treated as an engineering pattern, not just a setup trick. Cursor keeps the developer moving,&amp;nbsp;SQLcl&amp;nbsp;MCP keeps database access explicit, and Oracle AI Database keeps the evidence durable enough to inspect later.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The result is a workflow a team can inspect. You can see what Cursor asked for, which tool path ran, what the database allowed, which memory records were retrieved, and how the&amp;nbsp;final answer&amp;nbsp;was assembled.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;That is the shift that matters: from assistant access that is implicit and hard to audit, to explicit boundaries, durable memory, and evidence a developer can&amp;nbsp;actually debug. For database-connected development, that is what turns Cursor from a helpful local assistant into part of a controlled engineering workflow.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Frequently Asked Questions&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is MCP in this context?&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MCP is the protocol boundary that lets Cursor call explicit tools exposed by a server instead of accessing systems implicitly.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does MCP protect, and what does it not protect?&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MCP makes the tool interface explicit and reviewable: Cursor can&amp;nbsp;call only&amp;nbsp;the tools exposed by the server. It does not replace database security. The saved&amp;nbsp;SQLcl&amp;nbsp;connection profile, database user, grants, roles, network controls, and database policies&amp;nbsp;determine&amp;nbsp;what those tools can&amp;nbsp;actually access&amp;nbsp;or change.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why use&amp;nbsp;SQLcl&amp;nbsp;for Oracle MCP?&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SQLcl&amp;nbsp;already understands Oracle workflows and can run as the Oracle&amp;nbsp;SQLcl&amp;nbsp;MCP server&amp;nbsp;with sql -mcp, making the Oracle integration practical and direct.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this Cursor IDE or Cursor CLI?&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The workflow is Cursor CLI oriented because the notebook&amp;nbsp;validates&amp;nbsp;the&amp;nbsp;SQLcl&amp;nbsp;MCP path using a local MCP&amp;nbsp;serverdefinition and manual Cursor CLI checks. The same MCP server can be configured in project-scoped .cursor/mcp.json or global ~/.cursor/mcp.json, depending on how your team uses Cursor.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why include Oracle AI Database if MCP already works?&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MCP handles the execution boundary. Oracle AI Database handles durable memory, retrieval, vector search, concurrency, observability, and governance.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need an OpenAI API key?&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An external model-provider API key is only needed if you change the notebook to use a provider-backed embedding or LLM service. The default notebook path uses a local deterministic&amp;nbsp;embedder&amp;nbsp;so people can run the Agent Memory package section without adding billing details.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why include&amp;nbsp;LangChain&amp;nbsp;if Oracle already stores memory?&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because many teams already use&amp;nbsp;LangChain-shaped retrievers and chains. The notebook shows how Oracle-backed retrieval can fit that interface.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this RAG vs agent memory?&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not exactly. RAG retrieves external knowledge for a response, while agent memory preserves useful context, decisions, tool traces, and workflow state across sessions. In practice, production systems often use both.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the&amp;nbsp;minimum&amp;nbsp;viable&amp;nbsp;setup?&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SQLcl&amp;nbsp;MCP configured in Cursor CLI, one safe saved Oracle connection, and a read-only validation flow.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should production start with read-write permissions?&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Usually&amp;nbsp;no. Start read-only, log everything important, and add write scopes gradually with explicit approvals.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the best rollout strategy?&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pilot in development with read-only access and strong logging, then expand capabilities in controlled phases as the team learns which memory and tool paths are&amp;nbsp;actually useful.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Companion Troubleshooting Appendix&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Minimum&amp;nbsp;viable&amp;nbsp;setup:&lt;/strong&gt;&amp;nbsp;SQLcl&amp;nbsp;MCP configured in Cursor CLI, one approved Oracle connection, read-only validation, and database-side activity logging.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First checks: &lt;/strong&gt;confirm&amp;nbsp;sql&amp;nbsp;-mcp&amp;nbsp;starts, Cursor sees the tools after&amp;nbsp;restart, and the saved&amp;nbsp;SQLcl&amp;nbsp;connection alias resolves.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment model:&lt;/strong&gt; use separate credentials and policies for dev, test, and prod, with stricter controls as capability expands.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Logging model:&lt;/strong&gt; capture tool name, timestamp, thread ID, status, sanitized input/output summaries, and relevant&amp;nbsp;SQLcl&amp;nbsp;MCP log records.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieval quality:&lt;/strong&gt; tune chunk size, enrich metadata, review embedding choice, and evaluate retrieval against representative queries.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common anti-pattern:&lt;/strong&gt; expanding tool surfaces before ownership, logging standards, and runbooks are in place.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rollout path: &lt;/strong&gt;pilot in dev with read-only access and strong logging, then expand capabilities in controlled phases.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema:&lt;/strong&gt; Agent Memory package tables need a schema and tablespace that can create the package's JSON-backed objects.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scheduler job: &lt;/strong&gt;package-managed expiry purge needs scheduler-job privilege or managed setup for production.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model calls:&lt;/strong&gt; an API key can be present but still fail if the model provider account has no quota.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Resources&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.6/guide/get-started.html" rel="noreferrer noopener"&gt;Oracle AI Agent Memory documentation&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/" rel="noreferrer noopener"&gt;Oracle&amp;nbsp;AI&amp;nbsp;Database 26ai documentation&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/oracleagentmemory/" rel="noreferrer noopener"&gt;oracleagentmemory on PyPI&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.2/sqcug/sqlcl-mcp-server.html" rel="noreferrer noopener"&gt;SQLcl MCP Server&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/cursor_mcp_oracle_ai_database.ipynb" rel="noopener noreferrer"&gt;End-to-end&amp;nbsp;Companion notebook&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cursor</category>
      <category>mcp</category>
      <category>oracle</category>
      <category>database</category>
    </item>
    <item>
      <title>Hybrid Retrieval for Agent Memory: Vector, Lexical, and Metadata Together</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Fri, 28 Aug 2026 11:42:47 +0000</pubDate>
      <link>https://dev.to/oracledevs/hybrid-retrieval-for-agent-memory-vector-lexical-and-metadata-together-3com</link>
      <guid>https://dev.to/oracledevs/hybrid-retrieval-for-agent-memory-vector-lexical-and-metadata-together-3com</guid>
      <description>&lt;p&gt;&lt;strong&gt;Hybrid retrieval for agent memory is a multi-stage pipeline. Pure vector search is the prototype.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The companion repo implements the full pipeline against the schema and two-layer pattern from &lt;a href="https://blogs.oracle.com/developers/persistent-memory-and-derived-context-a-two-layer-pattern-for-agents" rel="noopener noreferrer"&gt;&lt;em&gt;Persistent Memory and Derived Context&lt;/em&gt;&lt;/a&gt;, &lt;a href="https://blogs.oracle.com/developers/from-prompt-to-persistence-part-1-designing-multi-tenant-agent-memory-schemas-for-saas" rel="noopener noreferrer"&gt;&lt;em&gt;From Prompt to Persistence part 1&lt;/em&gt;&lt;/a&gt;, and &lt;a href="https://blogs.oracle.com/developers/from-prompt-to-persistence-part-2-putting-the-multi-tenant-agent-memory-schema-to-work" rel="noopener noreferrer"&gt;&lt;em&gt;part 2&lt;/em&gt;&lt;/a&gt;: hybrid SQL with in-database PREDICTION reranking, a &lt;a href="https://pypi.org/project/langchain-oracledb/" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt; retriever, a &lt;a href="https://pypi.org/project/langgraph-oracledb/" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt; retrieval graph, and the evaluation suite with its golden-set labeling flow, runnable end to end against an Oracle AI Database 26ai Free instance on the 23-document research corpus built for this article. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Companion notebook: &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/hybrid_retrieval_pipeline.ipynb" rel="noopener noreferrer"&gt;Hybrid Retrieval Pipeline&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;a&gt;&lt;/a&gt;Key takeaways&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Hybrid retrieval for agent memory starts with database-enforced metadata filters. It then combines vector and lexical candidates through rank fusion, with optional reranking.&lt;/li&gt;



&lt;li&gt;Vector search finds semantic matches. Lexical search pins identifiers and exact strings. SQL and JSON metadata constrain scope and lifecycle before applying source and type restrictions. Those filters run before either search mode ranks a row.&lt;/li&gt;



&lt;li&gt;Every stage has to earn its place on a labeled set. In the companion experiment, equal-weight hybrid fusion underperformed vector search on the small corpus, while reranking improved ordering at a material latency cost.&lt;/li&gt;
&lt;/ul&gt;





&lt;p&gt;Jane's research assistant at Acme has good days and bad days. Some answers are sharp, cited, exactly right. Others are vaguely on-topic in a way that's hard to complain about but impossible to trust. There's no obvious pattern. Same model, same prompt template, same memory schema underneath. The team suspects the model, because the model is the part everyone watches.&lt;/p&gt;

&lt;p&gt;Retrieval is often the first place to look. When the right rows reach the context window, the model has a better chance of producing a grounded answer. When they do not, even a capable model may answer confidently from the wrong material. The model cannot recover a passage the retrieval pipeline never supplied.&lt;/p&gt;

&lt;p&gt;This is the third piece in a series. &lt;a href="https://blogs.oracle.com/developers/from-prompt-to-persistence-part-1-designing-multi-tenant-agent-memory-schemas-for-saas" rel="noopener noreferrer"&gt;From Prompt to Persistence part 1&lt;/a&gt; and &lt;a href="https://blogs.oracle.com/developers/from-prompt-to-persistence-part-2-putting-the-multi-tenant-agent-memory-schema-to-work" rel="noopener noreferrer"&gt;part 2&lt;/a&gt; built the multi-tenant schema: eight typed memory tables, four scope columns, row-level security on the tenant boundary, and provenance on every durable row. &lt;a href="https://blogs.oracle.com/developers/persistent-memory-and-derived-context-a-two-layer-pattern-for-agents" rel="noopener noreferrer"&gt;Persistent Memory and Derived Context&lt;/a&gt; separated the canonical layer from the derived layer so every retrieved row still points to something true.&lt;/p&gt;

&lt;p&gt;Part 2 ended with vector and lexical scores side by side, deliberately unfused. This article picks up there. The schema stores the memory. The two-layer pattern keeps it honest. This pipeline reads it and assembles the working set the agent uses.&lt;/p&gt;





&lt;h2&gt;
&lt;a&gt;&lt;/a&gt;Why your agent feels slightly off&lt;/h2&gt;

&lt;p&gt;The question we'll use throughout comes from the research assistant scenario the whole series has been building. Jane asks:&lt;/p&gt;

&lt;p&gt;"What did the Letta paper say about memory eviction, and how does that compare to what the AgentCore docs recommend?"&lt;/p&gt;

&lt;p&gt;This one sentence stresses every part of a retrieval system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Letta" and "AgentCore" are exact-match identifiers. Embedding models under-weight rare proper nouns, so vector search will happily return chunks about memory management from papers that never mention either name.&lt;/li&gt;



&lt;li&gt;"Memory eviction" is a concept. The Letta paper might call it context pruning, message eviction, or recursive summarization. Lexical search will miss every paraphrase.&lt;/li&gt;



&lt;li&gt;"The docs the user has access to" is a scope constraint. Only ingested papers and vendor docs in collections Jane's agent is allowed to read, inside Acme's tenant boundary, should be candidates at all. No similarity score enforces that. Only a filter does.&lt;/li&gt;



&lt;li&gt;Comparing two sources means the context window needs good chunks from both documents rather than five redundant chunks from whichever one embeds closer to the query.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run it through pure vector top-k against the research corpus the companion notebook builds and the answer surfaces AgentCore only incidentally, never the passage that answers the question. Run it through hybrid retrieval and both sources appear, with the first AgentCore retention passage at fused rank eleven. Reranking brings that passage to rank five, after four Letta passages. Context budgeting then preserves coverage from both sources so the agent can make the comparison Jane asked for.&lt;/p&gt;

&lt;p&gt;Same model every time. The thing that moved was retrieval. That's the thesis: retrieval is an architecture.&lt;/p&gt;





&lt;h2&gt;
&lt;a&gt;&lt;/a&gt;Vector, lexical, metadata: three retrieval modes, three failure modes&lt;/h2&gt;

&lt;p&gt;This pipeline uses three retrieval modes, and each one covers a failure mode the other two leave exposed. Whether both ranking modes earn a place depends on the corpus and query mix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vector retrieval&lt;/strong&gt; ranks by semantic similarity. It's the mode that handles paraphrase, synonyms, conceptual matches, the reason &lt;em&gt;memory eviction&lt;/em&gt; can find a chunk about "pruning stale messages from the context window." Its failure mode is specificity. Rare tokens, product names, error strings, version numbers, benchmark identifiers: all of them get diluted into an embedding that mostly encodes the surrounding topic. Ask a vector index about "Letta" and it hears "a paper about agent memory," which describes half the corpus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lexical retrieval&lt;/strong&gt; ranks by term matching using Oracle Text relevance scores. It's the mode that nails exact identifiers, code fragments, error messages typed verbatim. A user who pastes &lt;code&gt;ORA-51805&lt;/code&gt; into the chat deserves the document containing &lt;code&gt;ORA-51805&lt;/code&gt;, and no embedding model should be trusted with that job. Its failure mode is the inverse: any vocabulary shift between the query and the document breaks it. "Memory eviction" finds nothing in a paper that only ever says "context pruning."&lt;/p&gt;

&lt;p&gt;Metadata retrieval does not rank. It filters on scope, lifecycle, time, source, collection, and type. Those predicates become a security boundary only when the database enforces them with row-level security or an equivalent policy. An application filter is useful for retrieval scope, but it is not sufficient tenant isolation because a caller can forget it. A relevance score is an opinion. A database-enforced scope predicate is a boundary.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Strong at&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Fails at&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Role in the pipeline&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Vector&lt;/td&gt;
&lt;td&gt;Paraphrase, synonyms, concepts&lt;/td&gt;
&lt;td&gt;Rare terms, identifiers, exact strings&lt;/td&gt;
&lt;td&gt;Candidate generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lexical&lt;/td&gt;
&lt;td&gt;Identifiers, code, verbatim phrases&lt;/td&gt;
&lt;td&gt;Rephrasing, vocabulary drift&lt;/td&gt;
&lt;td&gt;Candidate generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata&lt;/td&gt;
&lt;td&gt;Scope, time, policy, source, type&lt;/td&gt;
&lt;td&gt;Ranking anything&lt;/td&gt;
&lt;td&gt;Filtering; security when policy-enforced&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Jane's question needs all three at once: metadata to bound the search to her tenant and collections, lexical to pin "Letta" and "AgentCore," vector to find eviction discussed in other words. Only the filter is mandatory for correctness — a missing scope predicate is a security bug, while a missing retriever is only a quality one.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FPicture1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FPicture1.png" alt="Diagram comparing vector, lexical, and metadata search. Vector ranks semantic similarity, lexical ranks term matches, and metadata filters scope, time, source, type, tenant ID, deletion, and validity; all contribute to candidate generation or filtering." width="364" height="1194"&gt;&lt;/a&gt;&lt;em&gt;Figure 1: Three retrieval modes and the failure mode each one covers for the others.&lt;/em&gt;&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;a&gt;&lt;/a&gt;The hybrid retrieval pipeline, stage by stage&lt;/h2&gt;

&lt;p&gt;The order of stages follows one principle borrowed directly from query optimization: &lt;strong&gt;filter early, rerank late&lt;/strong&gt;. Cheap, high-selectivity operations run first and shrink the candidate set. Expensive, high-precision operations run last, on the smallest set that still contains the answer. Databases have applied this thinking to query plans for fifty years. Retrieval pipelines are query plans, and most of them are written by people who've never looked at one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 1: query understanding.&lt;/strong&gt; Before anything touches an index, the raw question gets decomposed: extract entities ("Letta", "AgentCore"), expand the concept vocabulary (memory eviction to context pruning, message eviction, summarization), and classify intent. A factual lookup, a procedural recall, and an open-ended comparison want different candidate mixes from different tables. A production implementation can use one fast-model call that returns a small JSON object with entities and expanded terms, plus intent and time bounds. The companion notebook uses fixed rules and a small thesaurus for the same step. This keeps the demo repeatable and removes model-response variance from the retrieval comparison. The plan must still yield a valid lexical expression when a question has no proper nouns or thesaurus hits; the fallback is the question's own content words.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 2: metadata filtering.&lt;/strong&gt; Apply scope and policy before any ranking happens. In the schema from &lt;a href="https://blogs.oracle.com/developers/from-prompt-to-persistence-part-1-designing-multi-tenant-agent-memory-schemas-for-saas" rel="noopener noreferrer"&gt;&lt;em&gt;From Prompt to Persistence (Part 1)&lt;/em&gt;&lt;/a&gt;, the tenant predicate is appended by row-level security, so it's enforced even if every other stage of the pipeline has a bug. The remaining predicates are the working scope block from part 1 (&lt;code&gt;user_id&lt;/code&gt;/&lt;code&gt;agent_id&lt;/code&gt;/&lt;code&gt;thread_id &lt;/code&gt;with &lt;code&gt;NULL&lt;/code&gt; inheritance, &lt;code&gt;deleted_at IS NULL&lt;/code&gt;, &lt;code&gt;valid_until &lt;/code&gt;still open) plus whatever the query understanding stage produced: collection restrictions, time windows, source types. This stage is typically cheaper than ranking and can shrink the candidate set substantially when the predicates are selective. Every row it removes is a row the expensive stages never touch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 3: hybrid candidate generation.&lt;/strong&gt; Vector search and lexical search run against the filtered set, each producing its own ranked top-k (50 is a reasonable default for each side). They disagree with each other constantly, and that's the point. The vector pool has the paraphrases. The lexical pool has the identifiers. The answer to Jane's question lives in the union.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 4: score fusion.&lt;/strong&gt; Vector distances and lexical scores live on incompatible scales, so don't combine the scores. Combine the ranks. Reciprocal rank fusion (RRF) gives each candidate &lt;code&gt;1/(60 + rank)&lt;/code&gt; from each list it appears in and sums them. RRF avoids score calibration and has one constant, conventionally set to 60. A calibrated linear combination can outperform it when measured score distributions support the added complexity, so treat RRF as a strong baseline rather than a guaranteed winner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 5: reranking.&lt;/strong&gt; A cross-encoder scores the query and candidate together, which can improve ordering after candidate generation. It is also the most expensive stage, so it runs on 40 candidates instead of the full corpus. In the companion evaluation, this stage produced the largest NDCG gain, along with roughly 2.2 seconds of p50 added latency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 6: context budgeting.&lt;/strong&gt; The token budget is fixed before retrieval starts, and the reranked list gets cut to fit it. The notebook uses a greedy budget pass instead of simple truncation: prefer diversity across source documents for a comparison question, drop near-duplicate chunks even when they score well, and stop early when scores fall off a cliff. More tokens of bad context is just more expensive bad context, and evaluations of long-context models show that task performance can drop when relevant material appears in the middle, exactly where a lazy pipeline dumps its marginal hits. What you didn't include is a decision too. The output of this stage is the working set: the ranked, budgeted, provenance-tagged slice of memory that the agent loop's Infer &amp;amp; Act phase actually consumes. How that working set gets ordered and laid out inside the prompt is the next article in this series; this pipeline's job ends at deciding what earned a seat.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FPicture2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FPicture2.png" alt="Vertical workflow showing query understanding, metadata filtering, candidate generation from vector and lexical pools, score fusion, reranking, and context budgeting. Candidate counts shrink from all documents to 136, 100, 40, 40, and 7." width="586" height="1228"&gt;&lt;/a&gt;&lt;em&gt;Figure 2: The six-stage pipeline. Candidate counts shrink left to right; per-candidate cost grows.&lt;/em&gt;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;One thing worth making explicit, because it connects this article to the last one: the pipeline reads the derived layer, and only the derived layer, for ranking. Embeddings, text indexes, materialized projections: all of it is derived context, rebuilt from canonical memory under the sync policies from the two-layer article. The metadata filters run against canonical scope and lifecycle columns. Every candidate that survives to the working set carries its provenance (&lt;code&gt;source_event_id&lt;/code&gt;, document ID, version) back to a canonical row. Fast path for ranking with true path for citation. The arrow between them points one direction.&lt;/p&gt;





&lt;h2&gt;
&lt;a&gt;&lt;/a&gt;The hybrid query in one SQL statement&lt;/h2&gt;

&lt;p&gt;Now the centerpiece. Stages 2 through 5 (metadata filtering, both candidate generators, RRF fusion, and the cross-encoder rescore) as one SQL statement against the knowledge base tables from part 1. Not an integration project. A query.&lt;/p&gt;

&lt;p&gt;One piece of DDL is needed on top of the part 1 schema. Entity and summarization memory already carry Oracle Text indexes for their lexical halves; the knowledge base chunks need the same:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CREATE INDEX idx_kb_chunk_text ON knowledge_base_chunk (content)
  INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS ('SYNC (ON COMMIT)');&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With that in place, here's Jane's question as a query plan. &lt;code&gt;:query&lt;/code&gt; is the natural-language question; &lt;code&gt;:lex_query&lt;/code&gt; is the Oracle Text expression the query understanding stage built from the extracted entities and expanded terms (something like &lt;code&gt;{Letta} OR {AgentCore} OR {memory eviction} OR {context pruning}&lt;/code&gt;. Brace every term. Oracle Text reserves &lt;code&gt;ABOUT&lt;/code&gt;, &lt;code&gt;ACCUM&lt;/code&gt;, &lt;code&gt;AND&lt;/code&gt;, &lt;code&gt;NEAR&lt;/code&gt;, &lt;code&gt;NOT&lt;/code&gt; and &lt;code&gt;WITHIN&lt;/code&gt; as operators, so an ordinary English word lifted from a user's question is a syntax error unescaped. The question "where do models attend least within a long context?" fails on &lt;code&gt;within&lt;/code&gt;. Braces make the content literal, which also handles hyphens, and it saves maintaining a reserved-word list that has to track Oracle's):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;WITH
-- Stage 3a: vector candidates over the metadata-filtered set
vec_pool AS (
  SELECT c.id,
         ROW_NUMBER() OVER (
           ORDER BY VECTOR_DISTANCE(c.embedding,
             VECTOR_EMBEDDING(ALL_MINILM_L12_V2 USING :query AS DATA),
             COSINE)) AS vec_rank
    FROM knowledge_base_chunk c
    JOIN knowledge_base_document d ON d.id = c.document_id
   WHERE d.collection IN ('ingested-papers', 'vendor-docs')     -- Stage 2 starts here
     AND d.valid_until IS NULL AND d.deleted_at IS NULL
     AND c.deleted_at IS NULL
     AND (d.user_id IS NULL OR d.user_id = :user_id)
     AND JSON_VALUE(c.metadata, '$.section_type') &amp;lt;&amp;gt; 'references'
   ORDER BY VECTOR_DISTANCE(c.embedding,
             VECTOR_EMBEDDING(ALL_MINILM_L12_V2 USING :query AS DATA),
             COSINE)
   FETCH FIRST 50 ROWS ONLY
),
-- Stage 3b: lexical candidates over the same filtered set
lex_pool AS (
  SELECT c.id,
         DENSE_RANK() OVER (ORDER BY SCORE(1) DESC) AS lex_rank
    FROM knowledge_base_chunk c
    JOIN knowledge_base_document d ON d.id = c.document_id
   WHERE CONTAINS(c.content, :lex_query, 1) &amp;gt; 0
     AND d.collection IN ('ingested-papers', 'vendor-docs')
     AND d.valid_until IS NULL AND d.deleted_at IS NULL
     AND c.deleted_at IS NULL
     AND (d.user_id IS NULL OR d.user_id = :user_id)
     AND JSON_VALUE(c.metadata, '$.section_type') &amp;lt;&amp;gt; 'references'
   ORDER BY SCORE(1) DESC
   FETCH FIRST 50 ROWS ONLY
),
-- Stage 4: reciprocal rank fusion across the two pools
fused AS (
  SELECT COALESCE(v.id, l.id) AS id,
         COALESCE(1 / (60 + v.vec_rank), 0)
       + COALESCE(1 / (60 + l.lex_rank), 0) AS rrf_score
    FROM vec_pool v
    FULL OUTER JOIN lex_pool l ON v.id = l.id
),
candidates AS (
  SELECT id, rrf_score
    FROM fused
   ORDER BY rrf_score DESC
   FETCH FIRST 40 ROWS ONLY
)
SELECT c.id, c.content, c.chunk_index, c.metadata,
       d.id AS document_id, d.title, d.source_uri, d.version,
       k.rrf_score,
       PREDICTION(BGE_RERANKER USING :query AS FIRST_INPUT,
                  d.title || '. ' || c.content AS SECOND_INPUT) AS rerank_score
  FROM candidates k
  JOIN knowledge_base_chunk c    ON c.id = k.id
  JOIN knowledge_base_document d ON d.id = c.document_id
 ORDER BY rerank_score DESC;
-- tenant_id predicate appended automatically by RLS on every table reference
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Walking the clauses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This example demonstrates the inherited &lt;code&gt;user_id&lt;/code&gt; scope predicate inside each pool. The tenant predicate does not appear because it cannot be forgotten: RLS appends it to every table reference, in both pools and on the final join, every time. Apply corresponding &lt;code&gt;agent_id&lt;/code&gt; or &lt;code&gt;thread_id&lt;/code&gt; predicates when knowledge-base documents use those scopes; this shared research corpus is tenant- and user-scoped. That holds only if the policy loop from part 1 matches &lt;code&gt;KNOWLEDGE_BASE%&lt;/code&gt; as well as &lt;code&gt;%_MEMORY&lt;/code&gt;. Both knowledge-base tables carry &lt;code&gt;tenant_id&lt;/code&gt; but neither name ends in &lt;code&gt;_MEMORY&lt;/code&gt;, so an obvious loop leaves precisely the two tables this query reads without a policy, and the query has no predicate of its own to fall back on.&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;JSON_VALUE(c.metadata, '$.section_type')&lt;/code&gt; is a derived-metadata filter, here excluding bibliography chunks that match "Letta" forty times without saying anything. Part 1's functional index pattern on JSON paths makes this predicate indexable rather than a per-row parse.&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;VECTOR_DISTANCE&lt;/code&gt; with an in-database &lt;code&gt;VECTOR_EMBEDDING&lt;/code&gt; means the query text is embedded by the engine in the statement itself, with the HNSW (&lt;a href="https://en.wikipedia.org/wiki/Hierarchical_navigable_small_world" rel="noopener noreferrer"&gt;&lt;em&gt;Hierarchical Navigable Small World&lt;/em&gt;&lt;/a&gt;) index from part 1 (&lt;code&gt;ORGANIZATION INMEMORY NEIGHBOR GRAPH&lt;/code&gt;) available to the optimizer. On this 137-chunk run, Oracle chose a different physical plan.&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;CONTAINS ... SCORE(1)&lt;/code&gt; is the Oracle Text half, running against the index we just created. Note that both pools repeat the same metadata predicates: each generator ranks only what the filters allow, which is what &lt;em&gt;filter early&lt;/em&gt; means in practice. Note also that the lexical pool ranks with &lt;code&gt;DENSE_RANK&lt;/code&gt; rather than &lt;code&gt;ROW_NUMBER&lt;/code&gt;. Oracle Text &lt;code&gt;SCORE() &lt;/code&gt;is a coarse integer, and on short chunks ties are the norm rather than the exception: one query against the companion notebook's corpus matched 80 chunks across ten distinct scores. &lt;code&gt;ROW_NUMBER&lt;/code&gt; would order those tied chunks arbitrarily, and the next stage would read that arbitrary order as signal. This is the sharp edge on rank fusion generally: discarding the scores and trusting the order is what makes RRF insensitive to score scaling, and equally what makes it credulous when the order is partly noise.&lt;/li&gt;



&lt;li&gt;The &lt;code&gt;FULL OUTER JOIN&lt;/code&gt; plus &lt;code&gt;COALESCE&lt;/code&gt; arithmetic is RRF, in four lines. A chunk found by both generators gets both reciprocal terms and rises; a chunk found by only one still competes. This is the fusion that part 2's query deferred: where that statement returned &lt;code&gt;vec_score&lt;/code&gt; and &lt;code&gt;lex_score&lt;/code&gt; side by side and left the ranking to a later stage, this is the later stage.&lt;/li&gt;



&lt;li&gt;The &lt;code&gt;candidates&lt;/code&gt; CTE caps the set at 40 rows before reranking, which is Stage 5's input contract.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CTEs describe the logical stages. The optimizer still chooses the physical plan. In the companion run, the 137-chunk corpus was small enough that Oracle used the collection and chunk indexes, window sorts, and a full outer hash join without an HNSW step. A larger corpus may produce a different plan. Read the plan Oracle chose before tuning the plan you expected.&lt;/p&gt;

&lt;p&gt;The plan below is condensed from the companion notebook's &lt;code&gt;DBMS_XPLAN&lt;/code&gt; output:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SORT ORDER BY STOPKEY
 NESTED LOOPS
  HASH JOIN FULL OUTER (RRF fusion)
   WINDOW SORT PUSHED RANK (vector pool)
    INDEX RANGE SCAN IDX_KB_DOC_COLLECTION
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And one property that's easy to miss because nothing visibly does it: the whole statement reads &lt;strong&gt;one transactional snapshot&lt;/strong&gt;. The vector pool, the lexical pool, the final join: all of them see the same instant of the database. If a document is being superseded mid-query by a Promote step on another connection, this query sees it entirely old or entirely new, never a chunk from each. After the two-layer article made the case for keeping canonical and derived in sync, this is the read-side payoff: consistency you don't write code for.&lt;/p&gt;

&lt;h3&gt;
&lt;a&gt;&lt;/a&gt;The polyglot version, for contrast&lt;/h3&gt;

&lt;p&gt;The same pipeline with a dedicated vector store, Elasticsearch for lexical, and Postgres for scope looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# 1. Resolve scope: which document IDs may this user see? (Postgres)
doc_ids = pg.fetch_allowed_documents(tenant_id, user_id,
                                 collections=['ingested-papers', 'vendor-docs'])

# 2. Vector candidates (vector store), filtered to allowed docs
vec_hits = vector_store.query(vector=embed(query), top_k=50,
                              filter={'tenant_id': tenant_id,
                                      'document_id': {'$in': doc_ids}})

# 3. Lexical candidates (Elasticsearch), same filter re-expressed in its DSL
lex_hits = es.search(index='kb_chunks', size=50, query={
    'bool': {'must':   {'query_string': {'query': lex_query}},
             'filter': [{'term':  {'tenant_id': tenant_id}},
                        {'terms': {'document_id': doc_ids}}]}})

# 4. RRF, in application code
fused = rrf_merge(vec_hits, lex_hits, k=60)[:40]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Four numbered steps, three systems, and every number hides a cost. Three round trips run serially because each depends on the last, so network and service latency accumulate before the application can fuse the results. The tenant boundary is enforced three times in three different filter dialects, and each one is a place it can be forgotten; the schema articles made the case that this is the failure mode that matters most in multi-tenant SaaS. The $in filter ships potentially thousands of document IDs over the wire because the systems can't join. There's no shared snapshot, so a document superseded between step 2 and step 3 can contribute its old chunks to one pool and its new chunks to the other. And the RRF merge is now application code you own and debug at 2 AM.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Concern&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Converged SQL pipeline&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Three-system pipeline&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Application round trips&lt;/td&gt;
&lt;td&gt;One statement through reranking&lt;/td&gt;
&lt;td&gt;Three serial calls before application fusion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tenant enforcement&lt;/td&gt;
&lt;td&gt;One database policy model&lt;/td&gt;
&lt;td&gt;Repeated in three filter dialects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data crossing boundaries&lt;/td&gt;
&lt;td&gt;Candidate data stays in the database through fusion&lt;/td&gt;
&lt;td&gt;IDs and candidates cross service boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read consistency&lt;/td&gt;
&lt;td&gt;One transactional snapshot&lt;/td&gt;
&lt;td&gt;No shared snapshot across systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fusion code&lt;/td&gt;
&lt;td&gt;SQL in the query&lt;/td&gt;
&lt;td&gt;Application code to build and operate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency and service cost&lt;/td&gt;
&lt;td&gt;Measure the deployed database and reranker&lt;/td&gt;
&lt;td&gt;Measure network, service, and merge costs together&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None of these are exotic failures. They are the ordinary tax of putting a join across a network boundary. When retrieval can live in SQL, that tax disappears into the engine, and there's no merge layer to build or operate. Sidecars and dedicated stores can still be the right choice when hardware, latency, or existing infrastructure justifies the additional boundary.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FPicture3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FPicture3.png" alt="Comparison diagram: the converged approach applies row-level scope once, combines vector and lexical pools with a full outer join and prediction rerank in one round trip; the polyglot approach filters Postgres, a vector store, and Elasticsearch separately before application-level merging." width="390" height="1198"&gt;&lt;/a&gt;&lt;em&gt;Figure 3: One statement, one snapshot, one security model versus three systems, three filters, an application-layer merge.&lt;/em&gt;&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;a&gt;&lt;/a&gt;Reranking: precision after candidate generation&lt;/h2&gt;

&lt;p&gt;Everything so far uses bi-encoders: the query and every chunk were embedded separately, at different times, with no knowledge of each other, and relevance was approximated by the distance between those independent points. That's what makes candidate generation cheap and scalable, and it's also the ceiling on its precision.&lt;/p&gt;

&lt;p&gt;A cross-encoder can improve on that approximation by scoring the pair together. Query and candidate go through the model in one pass, attention flows between their tokens, and the output is a single relevance score computed with full knowledge of both texts. It can see that a chunk mentioning &lt;em&gt;message eviction policy in MemGPT-style agents&lt;/em&gt; answers a question about &lt;em&gt;memory eviction in the Letta paper&lt;/em&gt; (same lineage, different names), and that a chunk merely containing the word "Letta" in a citation list answers nothing. Bi-encoders can't make either judgment. This is also why cross-encoders can't generate candidates: scoring every chunk in the corpus against every query is a full scan of the most expensive kind. They only work as the late, narrow stage of a funnel that something cheaper built.&lt;/p&gt;

&lt;p&gt;Placement and sizing follow from that. The reranker runs after fusion and before budgeting, on the fused top-N. Size N generously enough to recover from candidate-generation mistakes (the right chunk at fused rank 35 can still be rescued) and small enough to afford: The notebook uses 40. Choose a smaller or larger candidate set from measured recall and latency on the deployed workload. Reranking cannot fix candidate-set recall. If the answer isn't in the candidate set, no amount of rescoring surfaces it, which is why the generators cast a wide net and the reranker exists to clean it up.&lt;/p&gt;

&lt;p&gt;One implementation detail earns its own paragraph, because getting it wrong is silent. Rerank the chunk with its document title prepended. Chunks are short, and a cross-encoder handed a bare sentence has nothing telling it which document that sentence came from. On the companion notebook's corpus, scoring the chunk text alone lost document context the model needed. For one tenant-isolation question, the best Multi-Tenant chunk ranked 11th; the next two landed at 21st and 24th. Prepending the title moved all three into the top three. The fix is &lt;code&gt;d.title || '. ' || c.content&lt;/code&gt; where you would otherwise have passed &lt;code&gt;c.content&lt;/code&gt;. Nothing in the output announces when you have this wrong, which is the first argument for the evaluation section below.&lt;/p&gt;

&lt;p&gt;For the reference implementation, run a small open-source cross-encoder in the database as a &lt;code&gt;PREDICTION&lt;/code&gt; expression so stages 2 through 5 remain one SQL statement and candidate text stays inside the database boundary. The companion notebook uses &lt;code&gt;BAAI/bge-reranker-base&lt;/code&gt; because it exported and loaded successfully in the tested Oracle AI Database 26ai and OML4Py 2.1.1 environment. The notebook also demonstrates the documented &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/utl_to_rerank-dbms_vector_chain.html" rel="noopener noreferrer"&gt;&lt;code&gt;DBMS_VECTOR_CHAIN.UTL_TO_RERANK&lt;/code&gt;&lt;/a&gt; interface. Treat model compatibility and size limits as release-specific.&lt;/p&gt;

&lt;p&gt;In the latest clean 36-query evaluation, the reranked path took 2,121 ms at p50 on the tested CPU, compared with 8 ms for pure vector search: 2,113 ms of added latency. Recent standalone 40-candidate runs took about 2.9–3.1 seconds after model warmup. These measurements cover different query sets and are not latency guarantees. &lt;code&gt;BAAI/bge-reranker-v2-m3&lt;/code&gt; exported to a 2.2 GB artifact and failed to load in the tested environment. Report those observed errors with the environment and release instead of turning them into a permanent product limit. Hosted rerankers send candidate chunks to a third party. A sidecar can be a valid latency or hardware choice, but it moves tenant text across an additional trust boundary and needs its own isolation, access controls, and observability.&lt;/p&gt;

&lt;p&gt;On the companion notebook's illustrative, purpose-built corpus of 23 documents and 137 chunks, with a labeled golden set built the way the evaluation section below describes, the three configurations produce the following results. The passages are paraphrases written for the notebook, so treat this as a worked retrieval experiment rather than a general benchmark:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;NDCG@10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Recall@20&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;p50 added latency&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pure vector top-k&lt;/td&gt;
&lt;td&gt;0.61&lt;/td&gt;
&lt;td&gt;0.76&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid (RRF)&lt;/td&gt;
&lt;td&gt;0.56&lt;/td&gt;
&lt;td&gt;0.69&lt;/td&gt;
&lt;td&gt;+10 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid + rerank&lt;/td&gt;
&lt;td&gt;0.71&lt;/td&gt;
&lt;td&gt;0.78&lt;/td&gt;
&lt;td&gt;~+2.1 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
&lt;a&gt;&lt;/a&gt;Experiment scope&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;23 documents and 137 chunks, or roughly six chunks per document on average&lt;/li&gt;



&lt;li&gt;Short, paraphrased passages written for the notebook&lt;/li&gt;



&lt;li&gt;36 labeled queries and 106 graded labels&lt;/li&gt;



&lt;li&gt;One embedding model, one fusion policy, one reranker, and one database environment&lt;/li&gt;



&lt;li&gt;Per-query NDCG@10 variation large enough that small metric differences should be treated as noise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read the table columnwise. On this small, short-chunk corpus, hybrid costs more than it returns: the lexical pool adds a weak, tie-heavy signal that equal-weight RRF then uses to dilute the vector ranking. That configuration result does not establish a universal verdict on hybrid retrieval. Run the same ablation on your own labeled corpus, with the same lexical tie policy in the hybrid and hybrid-plus-rerank paths, before attributing a gain to reranking alone. Reranking cannot improve candidate recall at 40 because it only reorders the set it receives. It can improve Recall@20 by moving relevant candidates above that cutoff, and it can improve NDCG because ordering is what the context budget consumes. Two seconds is a serious cost in an interactive agent, and it is the price of keeping tenant text inside the database on CPU. If that budget does not fit, a GPU or sidecar is a deployment trade-off; either introduces an additional trust boundary that needs its own tenant controls.&lt;/p&gt;





&lt;h2&gt;Integrating the pipeline with &lt;a href="https://pypi.org/project/langchain-oracledb/" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt; and &lt;a href="https://pypi.org/project/langgraph-oracledb/" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;A framework doesn't write your retrieval pipeline. It gives you a place to put it. A well-defined place to put things is useful, as long as nobody mistakes the socket for the appliance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/langchain-oracledb/" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt;'s retriever interface is the right entry point, and the integration follows the same pattern as the OracleVS integration in the Oracle &lt;a href="https://pypi.org/project/langchain-oracledb/" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt; modules: a custom retriever that wraps the SQL pipeline and goes through the Memory Manager, because part 2's rule hasn't changed. The manager is the only code allowed to touch the schema, and a framework adapter doesn't get an exemption:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from langchain_core.retrievers import BaseRetriever
from langchain_core.documents import Document

class OracleHybridRetriever(BaseRetriever):
    """LangChain entry point for the SQL retrieval pipeline."""

    manager: object     # from part 2: the one door into the schema
    user_id: str                 # request scope passed to the manager
    k: int = 8                 # final results after budgeting
    candidates: int = 40       # fused candidates reranked in the SQL statement

    def _get_relevant_documents(self, query: str, *, run_manager) -&amp;gt; list[Document]:
        plan = self.manager.understand_query(query)                 # stage 1
        rows = self.manager.search_knowledge_base(                  # stages 2-5: one SQL statement
            plan, user_id=self.user_id, top_k=self.candidates)
        working_set, _ = budget_context(                        # stage 6
            rows, max_tokens=plan["token_budget"], diversify_by=plan["diversify_by"])
        return [Document(page_content=r["content"],
                         metadata={"title": r["title"], "source": r["source_uri"],
                                   "document_id": r["document_id"],
                                   "version": r["version"],
                                   "score": r["rerank_score"]})
                for r in working_set[: self.k]]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That class drops into anything expecting a &lt;a href="https://pypi.org/project/langchain-oracledb/" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt; retriever. The consuming chain does not change when the retrieval strategy changes; whether the answers improve is a result for the evaluation suite rather than an assumption built into the integration.&lt;/p&gt;

&lt;p&gt;When the pipeline needs observable stages (and in production it does) &lt;a href="https://pypi.org/project/langgraph-oracledb/" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt; is the better frame. One node per stage, explicit state between nodes, and a graph you can checkpoint and instrument:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from typing import TypedDict
from langgraph.graph import StateGraph, START, END

class RetrievalState(TypedDict):
    query: str
    plan: dict          # stage 1 output: entities, expanded terms, intent, budget
    candidates: list    # stages 2-5 output: reranked, provenance-tagged rows
    working_set: list   # stage 6 output: what Infer &amp;amp; Act consumes

g = StateGraph(RetrievalState)
g.add_node('understand', understand_query)      # stage 1: one fast-model call
g.add_node('retrieve',   retrieve_and_rerank)   # stages 2-5: one SQL statement
g.add_node('budget',     budget_context)        # stage 6: pure function

g.add_edge(START, 'understand')
g.add_edge('understand', 'retrieve')
g.add_edge('retrieve', 'budget')
g.add_edge('budget', END)

retrieval = g.compile()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice the graph has three nodes for a six-stage pipeline. Stages 2 through 5 are one node because they are one SQL statement; the database already turned stages 2 through 5 into a single plan. Re-separating them in the orchestration layer would add round trips to recreate a boundary the engine deliberately erased. Oracle AI Database does the retrieval and reranking. &lt;a href="https://pypi.org/project/langgraph-oracledb/" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt; orchestrates the state around it.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FPicture4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FPicture4.png" alt="Flow diagram from LangGraph StateGraph through understand, retrieve, and budget nodes. Query understanding is stage 1; retrieval runs metadata filtering, candidate generation, score fusion, and reranking as stages 2 to 5 in one SQL statement; context budgeting is stage 6." width="800" height="1055"&gt;&lt;/a&gt;&lt;em&gt;Figure 4: The LangGraph pipeline. Three nodes, six stages, because the database collapsed stages 2 through 5.&lt;/em&gt;&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;a&gt;&lt;/a&gt;Evaluating retrieval quality&lt;/h2&gt;

&lt;p&gt;Every number in this article came from somewhere specific, because unmeasured retrieval is unmanaged retrieval. Teams that eyeball a few answers after each change are tuning by anecdote, and anecdotes have terrible recall.&lt;/p&gt;

&lt;p&gt;The metrics worth knowing, and why they’re important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recall@k measures how many truly relevant items made the top k. It is the candidate-generation metric. If recall@50 is poor, measure it first and at the fused-candidate boundary because downstream stages cannot recover missing candidates.&lt;/li&gt;



&lt;li&gt;Precision@k measures how many of the top k were relevant. It matters most at the working-set boundary, where every irrelevant item consumes context budget.&lt;/li&gt;



&lt;li&gt;MRR measures how high the first relevant item lands. Use it when one good hit is enough, such as an exact-fact lookup against entity memory.&lt;/li&gt;



&lt;li&gt;NDCG@10 rewards putting the most relevant items highest, using graded rather than binary relevance, over roughly the number of results that fit a context budget. For agent memory retrieval, it captures the ordering that the budget stage consumes. If you track one metric, start with this one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Warm the reranker before you time it, the same as you would any model server. In recent notebook runs, the first scoring call took roughly 0.9–1.1 seconds. Report that one-time model load separately from warm-path query latency. Assert the range while you are at it. NDCG is normalized to [0, 1], so a value above 1 exposes an evaluation bug, most often an ideal DCG computed from the label list instead of from every gradeable chunk in the corpus. A metric with defined bounds provides a free correctness check on the measurement, and that check is worth more than the metric on the day it fires.&lt;/p&gt;

&lt;p&gt;The golden set is the part that sounds hard and isn't, because the schema already did the work. &lt;code&gt;conversation_memory&lt;/code&gt; is the flight recorder: every question, retrieval, answer, and downstream signal (a follow-up, a thumbs-down, a correction) sits in the trace, scoped and timestamped. Log the candidate IDs as a &lt;code&gt;retrieval_result&lt;/code&gt; event alongside the tool calls. Harvesting question/relevant-chunk pairs is a query against tables you already operate, followed by a labeling pass where a human (or a strong model, with spot checks) marks which retrieved chunks actually supported a good answer. Two hundred labeled questions can be a useful starting target, but the number you need depends on query variance and the regression size you need to detect. The companion notebook runs on a 36-question demonstration set, which is enough to see a large effect and not enough to split hairs. Production traffic is the test set you already have. You just have to harvest it.&lt;/p&gt;

&lt;p&gt;Cadence matters as much as metrics. Once per release is the minimum. On every PR that touches the pipeline is better, and it's cheap once the golden set exists, because the whole evaluation is a few hundred SQL statements and a scoring script. On every embedding model change it's mandatory, and this is non-negotiable in a way the others aren't: new embeddings re-shape the entire vector space, the two-layer article made the derived-layer rebuild an afternoon's work, and an afternoon is exactly enough time to ship a model that's better on the benchmark and worse on your corpus without noticing.&lt;/p&gt;

&lt;p&gt;LLM-as-judge deserves an accurate framing: it's a directional signal, useful for catching large regressions between releases and for triaging where to spend labeling effort, and it is not a benchmark. Judges drift with their prompts and inherit the biases of whatever model is judging. Use one to point the flashlight. Use the labeled golden set to make claims.&lt;/p&gt;





&lt;h2&gt;
&lt;a&gt;&lt;/a&gt;Beyond hybrid: where agentic retrieval comes in&lt;/h2&gt;

&lt;p&gt;Everything to this point is one-shot: a question goes in, the pipeline runs once, a working set comes out. The next evolution is agentic retrieval, where the model itself issues queries, reads intermediate results, decides what to fetch next, and stops when it has enough. This is the direction serious production systems are converging, and it reframes classical single-pass RAG as the legacy pattern: not wrong, just the special case where one round trip happens to be enough.&lt;/p&gt;

&lt;p&gt;Look back at Jane's question and you can see why. "What did Letta say about memory eviction, and how does that compare to AgentCore" is two retrievals wearing one question mark. The one-shot pipeline handles it, with the budget stage working to keep both sources represented. An agentic retriever handles it more naturally: query for the Letta paper's eviction discussion, read the result, notice the paper frames eviction as recursive summarization, then query the AgentCore docs for that specific mechanism, with vocabulary learned from the first result. The second query is better than anything Stage 1 could have written up front, because it was informed by an actual intermediate read.&lt;/p&gt;

&lt;p&gt;The part that matters architecturally: agentic retrieval doesn't replace the hybrid pipeline, it composes it. Every query the model issues (through the retriever tool the &lt;a href="https://pypi.org/project/langchain-oracledb/" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt; section just built) runs through the same six stages, with the same scope filters, RLS boundary, and reranker. The agent decides what to ask. The pipeline decides what to return, and enforces what may be returned. Getting that separation wrong, letting the model's queries bypass the pipeline and hit indexes directly, reopens every tenant-isolation hole the schema articles closed.&lt;/p&gt;

&lt;p&gt;Cost draws the boundary for when to use it. Each retrieval round is a model call plus a pipeline run, so an N-hop agentic retrieval multiplies both latency and spend by N, and N isn't known in advance. Compositional, multi-hop, exploratory questions warrant it. "What's our refund threshold" does not, and an agent that spends four round trips on it is burning money to feel thorough. The practical pattern is an escalation policy: the intent classification from Stage 1 routes simple lookups through one pipeline pass and releases the multi-hop loop only when the question's structure demands it, under an explicit budget of rounds and tokens.&lt;/p&gt;

&lt;p&gt;One question remains open at the end of this pipeline, and it's the next article. The working set this article produces still has to become a context window, and that assembly (what order, what framing, what gets the model's strongest attention) is a deterministic construction problem rather than another retrieval problem. There's a tempting non-answer that would skip it: context windows are a million tokens now, why not retrieve loosely and let the model sort it out? The lost-in-the-middle problem is the counterargument, and the next piece makes it fully: evaluations show that task performance can drop when relevant material sits in the middle of a long context. A larger window changes what fits, while position can still affect performance.&lt;/p&gt;





&lt;h2&gt;
&lt;a&gt;&lt;/a&gt;Where this leaves you&lt;/h2&gt;

&lt;p&gt;The schema articles established tenant-isolated memory with typed records. The two-layer piece separated canonical memory from derived context. This article shows how the agent retrieves from it: filter first, generate vector and lexical candidates, fuse and rerank them, then budget the working set.&lt;/p&gt;

&lt;p&gt;If you take one action from this piece, run the ablation yourself. Pull real questions from your traces and hold the model and prompt fixed. Compare the current pipeline against a vector-only baseline, then test hybrid fusion and reranking separately. Measure ranking quality and answer outcomes beside p50 and p95 latency. Keep only the stages that earn their cost on your corpus.&lt;/p&gt;





&lt;h2&gt;
&lt;a&gt;&lt;/a&gt;Frequently asked questions&lt;/h2&gt;

&lt;h3&gt;
&lt;a&gt;&lt;/a&gt;How do I implement hybrid search for AI agent memory?&lt;/h3&gt;

&lt;p&gt;Apply database-enforced metadata scope first, then generate vector and lexical candidate pools from the allowed rows. Fuse their ranks and optionally rerank the survivors before cutting the result to a context budget. Keep provenance on every returned chunk.&lt;/p&gt;

&lt;h3&gt;
&lt;a&gt;&lt;/a&gt;When should hybrid search be used instead of vector search alone?&lt;/h3&gt;

&lt;p&gt;Test it when queries contain identifiers, error codes, product names, or vocabulary that embeddings tend to blur. Do not assume it wins: on a small corpus with short chunks and a noisy lexical signal, vector search alone may rank better.&lt;/p&gt;

&lt;h3&gt;
&lt;a&gt;&lt;/a&gt;How do vector search, lexical search, and metadata filtering work together?&lt;/h3&gt;

&lt;p&gt;Metadata determines which rows may compete. Vector search finds semantic matches and paraphrases. Lexical search finds exact terms. Rank fusion combines the candidate lists, and reranking can improve their final order.&lt;/p&gt;

&lt;h3&gt;
&lt;a&gt;&lt;/a&gt;How do you evaluate whether hybrid retrieval improves AI agent memory?&lt;/h3&gt;

&lt;p&gt;Build a labeled set from real agent questions and hold the model and prompt fixed. Compare ablations with NDCG and recall, while tracking MRR, answer quality, and p50/p95 latency. Report corpus size and chunking details alongside candidate counts and uncertainty.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vectordatabase</category>
      <category>oracle</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>What we learned about letting agents into a production database</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Mon, 24 Aug 2026 10:48:04 +0000</pubDate>
      <link>https://dev.to/oracledevs/what-we-learned-about-letting-agents-into-a-production-database-4ehc</link>
      <guid>https://dev.to/oracledevs/what-we-learned-about-letting-agents-into-a-production-database-4ehc</guid>
      <description>&lt;p&gt;Oracle is bringing its agent memory and Model Context Protocol work to &lt;a href="https://events.linuxfoundation.org/agntcon-mcpcon-europe/" rel="noopener noreferrer"&gt;AGNTCon + MCPCon Europe&lt;/a&gt;, including a workshop showing developers how to use Oracle AI Database as durable memory for AI agents, and how to let agents query enterprise data without generating their own SQL.&lt;/p&gt;





&lt;p&gt;Someone in your business asks their agent what discount rate you should offer in APAC next quarter. It thinks for a moment and says 25%.&lt;/p&gt;

&lt;p&gt;Would you be confident to ship that answer?&lt;/p&gt;

&lt;p&gt;That question came up when we sat down with &lt;a href="https://www.linkedin.com/in/thatjeffsmith/" rel="noopener noreferrer"&gt;Jeff Smith&lt;/a&gt;, who leads much of our Model Context Protocol work at Oracle, to talk about what our customers are running into. We had expected a conversation about protocols and plumbing. It was mostly about trust.&lt;/p&gt;





&lt;h2&gt;Why does an agent need a database at all?&lt;/h2&gt;

&lt;p&gt;Start with the thing that makes agents hard to build. Language models are stateless. Ask one a question today and it answers as though it has never met you, because unless you send the entire history back with every request, it has no durable awareness of anything that came before.&lt;/p&gt;

&lt;p&gt;Fine for a chat window. It falls apart the moment you want an agent that runs for weeks, remembers what it decided in March, and can explain why. &lt;a href="https://blogs.oracle.com/developers/what-is-agent-memory-a-beginners-guide-for-ai-developers" rel="noopener noreferrer"&gt;Memory&lt;/a&gt; is what turns a stateless model into an application that survives contact with a real business.&lt;/p&gt;

&lt;p&gt;Agents need somewhere to keep what they learn, and once that memory holds real company information it belongs where the rest of your data already lives, under the same access rules, backups and auditing. That makes it a database problem, and we have been working on those since 1979, when Oracle shipped the first commercially available SQL database.&lt;/p&gt;

&lt;p&gt;It is why &lt;a href="https://blogs.oracle.com/database/introducing-oracle-ai-agent-memory-a-unified-memory-core-for-enterprise-ai-systems" rel="noopener noreferrer"&gt;Oracle AI Agent Memory&lt;/a&gt; extends Oracle AI Database into a persistent memory core for agents: working memory for the task in hand, long-term factual memory, and procedural memory for how a job actually gets done. Our researchers published &lt;a href="https://arxiv.org/abs/2607.13157" rel="noopener noreferrer"&gt;a paper on it&lt;/a&gt; in July, and teams across the company are building the connective tissue that lets agents reach it.&lt;/p&gt;

&lt;p&gt;Which brings you back to the question we opened with. Once an agent can reach your data, how far do you trust what it does with it?&lt;/p&gt;





&lt;h2&gt;What actually goes wrong when an agent talks to a database?&lt;/h2&gt;

&lt;p&gt;The failure people brace for is the agent writing broken SQL. That is not the failure that hurts.&lt;/p&gt;

&lt;p&gt;The one that hurts is syntactically perfect SQL returning a number that is confidently wrong. The agent goes through your schema, finds an employees table, a sales table, a partners table, works out how it thinks they relate, and comes back with an answer. The query runs clean. Nothing errors. The number is just wrong, because the agent decided “month” meant days since first purchase rather than a calendar month, and nobody catches it for a quarter.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2Fagntcon-sql-accuracy-1024x376.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2Fagntcon-sql-accuracy-1024x376.png" alt="A bar chart of execution accuracy on the BIRD text-to-SQL benchmark, as published on 21 August 2026. The best published model reaches 80.04 per cent and human performance sits at 92.96 per cent." width="800" height="294"&gt;&lt;/a&gt;BIRD benchmark graphic comparing best published execution accuracy with human performance.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Ask again tomorrow and it may write a different query. So you end up engineering your prompts harder and harder to coax good SQL out of a model that has no idea which of your forty tables finance actually trusts. On the &lt;a href="https://bird-bench.github.io/" rel="noopener noreferrer"&gt;BIRD benchmark&lt;/a&gt;, as published on 21 August 2026, the best model on the leaderboard reaches 80.04% execution accuracy against 92.96% for humans. Respectable engineering, and worth rechecking before you quote it, because leaderboard positions move. Either way, not a number you want underneath a pricing decision. &lt;/p&gt;





&lt;h2&gt;What if the agent did not write the query at all? &lt;/h2&gt;

&lt;p&gt;This is the part we keep coming back to at Oracle. Your business already solved this, years ago.&lt;/p&gt;

&lt;p&gt;Somewhere in your organisation, there are two dozen reports your analysts use to answer exactly these questions, and behind each is a query somebody wrote, argued about, checked and eventually trusted enough to put in front of the business.&lt;/p&gt;

&lt;p&gt;So our managed service lets you &lt;a href="https://docs.oracle.com/en-us/iaas/database-tools/doc/database-tools-sql-reports.html" rel="noopener noreferrer"&gt;publish those reports to the agent as governed MCP tools&lt;/a&gt;. Each one arrives with the same contract any other tool on the protocol gets: a name, a description the agent can reason about, typed inputs it has to fill, and access control deciding who may call it at all.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2Fagntcon-report-as-tool-1024x418.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2Fagntcon-report-as-tool-1024x418.png" alt="Three panels. The analyst's report is a query already argued about and trusted. It is published as a governed MCP tool with typed inputs, access control and fixed SQL underneath. The agent picks the report and fills in the inputs, and is never asked to write the query." width="800" height="327"&gt;&lt;/a&gt;Governed SQL tool design: an agent selects a trusted analyst report and supplies typed inputs.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The agent sees something like a sales trends report with clearly typed inputs for quarter, region and business unit. It picks one and fills in the inputs. It does not write a line of SQL. The query underneath is fixed, so the same question follows the same trusted query path every time. If the number moves, it is because the data moved, not because the model phrased the question differently on a Tuesday. And the path it followed is one your analysts already stand behind.&lt;/p&gt;

&lt;p&gt;The shift sounds small but is significant in practice. The agent no longer has to be clever enough to work out the right SQL. Only clever enough to pick the right report.&lt;/p&gt;

&lt;p&gt;We should be straight about how new this is. Not many people work this way yet and we are still learning where it breaks. Natural language to SQL works perfectly well on our stack and plenty of teams will keep using it. But reusing work your analysts already trust, rather than asking a model to reinvent it live, is worth arguing about in public. &lt;/p&gt;





&lt;h2&gt;Who is the agent, and what is it allowed to see?&lt;/h2&gt;

&lt;p&gt;The other half of every customer conversation is about security, and the industry has not been subtle about why.&lt;/p&gt;

&lt;p&gt;In May the United States National Security Agency published &lt;a href="https://media.defense.gov/2026/Jun/02/2003943289/-1/-1/0/CSI_MCP_SECURITY.PDF" rel="noopener noreferrer"&gt;seventeen pages on Model Context Protocol security&lt;/a&gt;, finding that adoption has outpaced the safeguards. More than forty security vulnerabilities have been filed against implementations of the protocol this year. As &lt;a href="https://www.linkedin.com/in/vipinsamar/" rel="noopener noreferrer"&gt;Vipin Samar&lt;/a&gt;, our senior vice president for database security, &lt;a href="https://blogs.oracle.com/database/oracle-deep-data-security-is-now-available-in-oracle-ai-database-26ai" rel="noopener noreferrer"&gt;put it&lt;/a&gt;, agents are increasingly acting as autonomous insiders.&lt;/p&gt;

&lt;p&gt;We treat an agent as what it is: another application that proves who it is before it gets anything. It signs in through the corporate identity system your staff already use, and it inherits that person’s permissions rather than getting a set of its own.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2Fagntcon-identity-and-reach-1024x378.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2Fagntcon-identity-and-reach-1024x378.png" alt="Three panels. The agent signs in through the corporate identity system and holds no standing credential of its own. Its permissions are inherited from the person it acts for, not granted separately. Row, column and cell policy is declared once in SQL and enforced whether the request comes from a person or an agent." width="799" height="295"&gt;&lt;/a&gt;AI agent access controls: corporate sign-in, inherited permissions, and SQL-enforced policy.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Underneath sits the database security we have been building for more than four decades. &lt;a href="https://www.oracle.com/security/database-security/features/deep-data-security/" rel="noopener noreferrer"&gt;Oracle Deep Data Security&lt;/a&gt; extends the access controls our customers already rely on rather than replacing them, expressing them as policies you declare in SQL instead of code you maintain. It reaches individual rows, columns and cells, so two people asking an agent the same question see only what each is cleared to see. Salaries stay hidden from those who should not see them, whether the request comes from a person or an agent acting for them.&lt;/p&gt;

&lt;p&gt;None of that makes prompt injection go away. It does not. What it decides is how much of your data is reachable when something does go wrong, which is the question your security team will actually ask.&lt;/p&gt;





&lt;h2&gt;Four ways to connect&lt;/h2&gt;

&lt;p&gt;There is no single Oracle connector for this, because there is no single way people run Oracle. &lt;a href="https://blogs.oracle.com/database/introducing-mcp-server-for-oracle-database" rel="noopener noreferrer"&gt;One runs locally on a developer’s machine&lt;/a&gt;, which is how most people meet this first. &lt;a href="https://www.oracle.com/autonomous-database/mcp-server/" rel="noopener noreferrer"&gt;One is built into Autonomous Database&lt;/a&gt; with nothing to install. &lt;a href="https://docs.oracle.com/en-us/iaas/releasenotes/database-tools/mcp.htm" rel="noopener noreferrer"&gt;One is a managed service in Oracle Cloud&lt;/a&gt; that also reaches Oracle databases running inside Amazon, Microsoft and Google’s clouds, at no extra charge. And where data stays in a customer’s own data centre, there is &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/26.2/ordig/configuring-model-context-protocol-mcp.html" rel="noopener noreferrer"&gt;a version they run themselves&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Alongside those we publish an &lt;a href="https://github.com/oracle/mcp" rel="noopener noreferrer"&gt;open repository of reference connectors&lt;/a&gt; for the wider Oracle estate, from MySQL to a toolkit that reads your database driver logs and tells you why last night got slow.&lt;/p&gt;





&lt;h2&gt;Come and talk to us in Amsterdam&lt;/h2&gt;

&lt;p&gt;Oracle is a Gold member of the &lt;a href="https://www.linuxfoundation.org/press/linux-foundation-announces-the-formation-of-the-agentic-ai-foundation" rel="noopener noreferrer"&gt;Agentic AI Foundation&lt;/a&gt;, the Linux Foundation body that now hosts the Model Context Protocol, and we are sponsoring &lt;a href="https://events.linuxfoundation.org/agntcon-mcpcon-europe/" rel="noopener noreferrer"&gt;AGNTCon + MCPCon Europe&lt;/a&gt; at the RAI in Amsterdam on 17 and 18 September.&lt;/p&gt;

&lt;p&gt;We’re also bringing a hands-on workshop. It walks through giving an agent durable memory with Oracle AI Database underneath and connecting the agent to that memory over the Model Context Protocol. If you are building AI applications and have not yet worked out where the database fits, that session is built for you.&lt;/p&gt;

&lt;p&gt;We will be in the Solutions Showcase for the rest of the event. Come and tell us what you are building, and we will talk through how to put a database behind it. If you think letting agents call pre-approved reports is the wrong shape, tell us that too. We are early enough in this to still be changing our minds.&lt;/p&gt;

&lt;p&gt;Learn more: &lt;a href="https://www.oracle.com/mcp/" rel="noopener noreferrer"&gt;oracle.com/mcp&lt;/a&gt; and &lt;a href="https://github.com/oracle/mcp" rel="noopener noreferrer"&gt;github.com/oracle/mcp&lt;/a&gt;&lt;/p&gt;





&lt;h2&gt;References&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Oracle AI Agent Memory, a unified memory core for enterprise AI systems: &lt;a href="https://blogs.oracle.com/database/introducing-oracle-ai-agent-memory-a-unified-memory-core-for-enterprise-ai-systems" rel="noopener noreferrer"&gt;https://blogs.oracle.com/database/introducing-oracle-ai-agent-memory-a-unified-memory-core-for-enterprise-ai-systems&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;Oracle Agent Memory as an Enterprise Memory Substrate for Long-Horizon AI Agents, July 2026: &lt;a href="https://arxiv.org/abs/2607.13157" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2607.13157&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;What is agent memory, a beginner’s guide for AI developers: &lt;a href="https://blogs.oracle.com/developers/what-is-agent-memory-a-beginners-guide-for-ai-developers" rel="noopener noreferrer"&gt;https://blogs.oracle.com/developers/what-is-agent-memory-a-beginners-guide-for-ai-developers&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;MCP Server for Oracle Database, announced 16 July 2025: &lt;a href="https://blogs.oracle.com/database/introducing-mcp-server-for-oracle-database" rel="noopener noreferrer"&gt;https://blogs.oracle.com/database/introducing-mcp-server-for-oracle-database&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;Autonomous AI Database MCP Server: &lt;a href="https://www.oracle.com/autonomous-database/mcp-server/" rel="noopener noreferrer"&gt;https://www.oracle.com/autonomous-database/mcp-server/&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;Managed MCP servers in Oracle Cloud Infrastructure, 12 May 2026: &lt;a href="https://docs.oracle.com/en-us/iaas/releasenotes/database-tools/mcp.htm" rel="noopener noreferrer"&gt;https://docs.oracle.com/en-us/iaas/releasenotes/database-tools/mcp.htm&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;Running reports as agent tools: &lt;a href="https://docs.oracle.com/en-us/iaas/database-tools/doc/database-tools-sql-reports.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en-us/iaas/database-tools/doc/database-tools-sql-reports.html&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;Self-managed streaming endpoint, Oracle REST Data Services 26.2: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/26.2/ordig/configuring-model-context-protocol-mcp.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/26.2/ordig/configuring-model-context-protocol-mcp.html&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;National Security Agency, Model Context Protocol security design considerations, 20 May 2026: &lt;a href="https://media.defense.gov/2026/Jun/02/2003943289/-1/-1/0/CSI_MCP_SECURITY.PDF" rel="noopener noreferrer"&gt;https://media.defense.gov/2026/Jun/02/2003943289/-1/-1/0/CSI_MCP_SECURITY.PDF&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;BIRD benchmark leaderboard, figures as published on 21 August 2026: &lt;a href="https://bird-bench.github.io/" rel="noopener noreferrer"&gt;https://bird-bench.github.io/&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;Agentic AI Foundation: &lt;a href="https://www.linuxfoundation.org/press/linux-foundation-announces-the-formation-of-the-agentic-ai-foundation" rel="noopener noreferrer"&gt;https://www.linuxfoundation.org/press/linux-foundation-announces-the-formation-of-the-agentic-ai-foundation&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;AGNTCon + MCPCon Europe: &lt;a href="https://events.linuxfoundation.org/agntcon-mcpcon-europe/" rel="noopener noreferrer"&gt;https://events.linuxfoundation.org/agntcon-mcpcon-europe/&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;Oracle MCP servers: &lt;a href="https://www.oracle.com/mcp/" rel="noopener noreferrer"&gt;https://www.oracle.com/mcp/&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;Oracle MCP reference connectors on GitHub: &lt;a href="https://github.com/oracle/mcp" rel="noopener noreferrer"&gt;https://github.com/oracle/mcp&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;Oracle Deep Data Security: &lt;a href="https://www.oracle.com/security/database-security/features/deep-data-security/" rel="noopener noreferrer"&gt;https://www.oracle.com/security/database-security/features/deep-data-security/&lt;/a&gt; and &lt;a href="https://blogs.oracle.com/database/oracle-deep-data-security-is-now-available-in-oracle-ai-database-26ai" rel="noopener noreferrer"&gt;https://blogs.oracle.com/database/oracle-deep-data-security-is-now-available-in-oracle-ai-database-26ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>techtalks</category>
      <category>oracle</category>
      <category>amsterdam</category>
      <category>agentcon</category>
    </item>
    <item>
      <title>How to Build a REST API for an AI Application</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Fri, 21 Aug 2026 13:46:30 +0000</pubDate>
      <link>https://dev.to/oracledevs/how-to-build-a-rest-api-for-an-ai-application-2afl</link>
      <guid>https://dev.to/oracledevs/how-to-build-a-rest-api-for-an-ai-application-2afl</guid>
      <description>&lt;p&gt;Build a REST API for an AI application that stays reliable when
inference is slow, retried, or connected to real data. Use a thin
FastAPI layer to validate and authenticate requests, create a durable
prediction job, return &lt;code&gt;202 Accepted&lt;/code&gt; with a status URL, and
let a worker handle model execution outside the HTTP request.&lt;/p&gt;

&lt;p&gt;This guide shows how to keep the public API provider-neutral, persist
job state with Oracle AI Database in the production path, stream
interactive responses when needed, and enforce tenant-aware access. It
is grounded in maintained Oracle AI Developer Hub proof cases for SSE
delivery, retrieval orchestration, and enterprise data access.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Illustrative patterns vs. maintained proof cases&lt;/strong&gt;&lt;br&gt;The code in this guide explains the API boundaries. The maintained proof cases later in the article show those boundaries in working applications. For production, use Oracle AI Database persistence, tenant-scoped access, a separate worker or durable queue, approved identity, retention, monitoring, and distributed rate limiting. The SQL, VPD, streaming, and &lt;code&gt;BackgroundTasks&lt;/code&gt; snippets are design guidance, not copy-and-paste deployment code.&lt;/p&gt;&lt;/blockquote&gt;

&lt;h2 id="key-takeaways"&gt;Key takeaways&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Build the REST API around a resource lifecycle: submit work, return
&lt;code&gt;202 Accepted&lt;/code&gt;, and expose a status URL.&lt;/li&gt;
&lt;li&gt;Validate bounded JSON with Pydantic before calling a model, and
generate an OpenAPI contract from the implementation.&lt;/li&gt;
&lt;li&gt;Keep model-provider credentials server-side and isolate
provider-specific behavior behind an adapter.&lt;/li&gt;
&lt;li&gt;Persist job state durably, scope every read to the authenticated
tenant, and support idempotent retries.&lt;/li&gt;
&lt;li&gt;Use a worker and durable queue for variable or long-running
inference; use synchronous responses only when the timing is
predictable.&lt;/li&gt;
&lt;li&gt;Treat FastAPI, Oracle AI Database, ORDS, OpenAI Responses API,
streaming, and queues as separate architectural boundaries with
different jobs.&lt;/li&gt;
&lt;li&gt;Test authentication, tenant isolation, input rejection, rate
limiting, provider failures, worker transitions, and the public OpenAPI
contract before deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="what-will-you-design-and-verify"&gt;What will you design and
verify?&lt;/h2&gt;

&lt;p&gt;You will design a small FastAPI service that accepts a prediction
request, authenticates the caller, returns &lt;code&gt;202 Accepted&lt;/code&gt;
with a &lt;code&gt;Location&lt;/code&gt; header, processes the request outside the
HTTP request, and exposes final state through
&lt;code&gt;GET /v1/predictions/{id}&lt;/code&gt;. The same service generates
&lt;code&gt;/docs&lt;/code&gt; and &lt;code&gt;/openapi.json&lt;/code&gt; automatically.&lt;/p&gt;

&lt;p&gt;Use the three maintained proof cases in the Developer Hub section to
validate the boundary that matters to your application: interactive
streaming, retrieval and orchestration, or identity-aware enterprise
data access.&lt;/p&gt;

&lt;p&gt;The rest of the article gives you the design checklist: thin routes,
clear resource contracts, provider adapters, asynchronous work,
streaming, identity, and governed durable state. The proof cases show
how those pieces behave together in maintained applications.&lt;/p&gt;

&lt;h2 id="what-are-the-best-practices-for-building-a-rest-api-for-an-ai-application"&gt;What
are the best practices for building a REST API for an AI
application?&lt;/h2&gt;

&lt;p&gt;Use a clear resource contract, validate inputs before model calls,
keep provider credentials server-side, choose synchronous or
asynchronous execution deliberately, persist job state, scope reads to
authenticated tenants, and expose OpenAPI plus safe errors. Test the
failure path as carefully as the successful model response.&lt;/p&gt;

&lt;p&gt;The recommended implementation applies those practices through five
boundaries:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;HTTP boundary:&lt;/strong&gt; FastAPI, Pydantic, OpenAPI,
authentication, rate limiting, and request IDs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Job boundary:&lt;/strong&gt; &lt;code&gt;POST /v1/predictions&lt;/code&gt;
creates a durable resource and returns &lt;code&gt;202 Accepted&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Worker boundary:&lt;/strong&gt; a separate process claims work and
calls a model adapter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data boundary:&lt;/strong&gt; Oracle AI Database stores the
request lifecycle and applies tenant scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider boundary:&lt;/strong&gt; the model integration is
replaceable and must not expose provider credentials to clients.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id="how-do-you-design-a-thin-rest-api-layer-for-an-ai-app"&gt;How do
you design a thin REST API layer for an AI app?&lt;/h2&gt;

&lt;p&gt;Keep the REST API thin: authenticate and validate at the HTTP edge,
create or read a job resource, and delegate model calls, retrieval, and
durable state to dedicated boundaries. The route should not contain
provider-specific logic, long-running inference, or authorization rules
that the data layer must enforce again.&lt;/p&gt;

&lt;p&gt;Treat inference as a resource lifecycle rather than a single
long-running HTTP function. The API accepts and records work, the worker
performs inference, and the client reads the resulting resource using a
stable status URL. In the production path, FastAPI owns the
application-facing boundary and Oracle AI Database owns durable
prediction-job state.&lt;/p&gt;

&lt;h3 id="which-endpoints-should-a-chatbot-rest-api-expose-in-fastapi"&gt;Which
endpoints should a chatbot REST API expose in FastAPI?&lt;/h3&gt;

&lt;p&gt;A chatbot REST API should separate conversation resources, messages,
prediction jobs, and optional streams. This keeps chat history
addressable, makes slow inference observable, and lets authorization
apply to each resource instead of hiding the entire lifecycle behind one
&lt;code&gt;/chat&lt;/code&gt; request.&lt;/p&gt;

&lt;p&gt;For a chatbot-style REST API, expose endpoints that map cleanly to
the same resource lifecycle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /v1/conversations/{conversation_id}/messages&lt;/code&gt; —
create a message and schedule the underlying prediction job.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /v1/conversations/{conversation_id}&lt;/code&gt; — return
conversation metadata and current state.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /v1/conversations/{conversation_id}/messages&lt;/code&gt; —
return authorized message history.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /v1/predictions/{id}&lt;/code&gt; — poll the durable job until
the model response is ready.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /v1/conversations/{conversation_id}/messages/{message_id}/stream&lt;/code&gt;
— optionally stream incremental output with Server-Sent Events
(SSE).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DELETE /v1/conversations/{conversation_id}&lt;/code&gt; — delete
conversation data according to the retention policy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The client-facing flow stays consistent: FastAPI authenticates and
creates work, a worker updates durable job state in Oracle AI Database,
and the client reads the response through a status URL or an authorized
stream.&lt;/p&gt;

&lt;p&gt;The pattern is useful when model inference may outlive the request
timeout, when a client needs reliable status information, or when an AI
application must retain request context beside governed business
data.&lt;/p&gt;

&lt;h2 id="why-oracle-ai-database-for-the-production-path"&gt;Why Oracle AI Database for the production path?&lt;/h2&gt;

&lt;p&gt;Oracle AI Database gives the production path one place to keep durable prediction-job state beside governed application data. It can scope work to a tenant, support reliable status reads and idempotency records, and apply data-access controls close to the rows that the API exposes.&lt;/p&gt;

&lt;h2 id="proof-cases-apply-the-api-pattern-to-a-real-ai-developer-job"&gt;Proof cases: apply the API pattern to a real AI developer job&lt;/h2&gt;

&lt;p&gt;The REST API pattern is useful only when it connects to work a developer actually has to ship. These three Oracle AI Developer Hub assets are not substitutes for this tutorial; each is a working proof case for one boundary in the design.&lt;/p&gt;

&lt;h3 id="job-ship-an-interactive-ai-experience-without-turning-the-route-into-the-agent"&gt;Job: ship an interactive AI experience without turning the route into the agent&lt;/h3&gt;

&lt;p&gt;Use &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/apps/appbooks/agent_harness_palo_stack" rel="noopener noreferrer"&gt;Total Recall&lt;/a&gt; when you need to move from a &lt;code&gt;POST /v1/chat&lt;/code&gt; prototype to an interactive application that streams progress while keeping the agent harness outside the browser. It is a FastAPI backend and browser app that streams over SSE, then exposes the surrounding concerns—context, retrieval, memory, tools, and traces—as separate layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this proves for an API design:&lt;/strong&gt; streaming is a delivery mechanism at the HTTP boundary; it does not eliminate the need for a stable resource model, server-side provider credentials, or durable state. Use this proof case when the product requirement is “show useful progress now” rather than “return one blocking response later.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run it now:&lt;/strong&gt; prepare Python 3.11+, a reachable Oracle AI Database with its required embedding model, and an approved model-provider credential; then follow the appbook setup and run &lt;code&gt;./run.sh&lt;/code&gt;. Its documented success signal is a local browser application with the FastAPI service available and status indicators for the database, harness, reranker fallback or availability, and configured model provider. Use the live trace and streamed chat to check that your route delivers incremental output without moving the harness into the client.&lt;/p&gt;

&lt;h3 id="job-make-a-retrieval-backed-ai-api-answer-from-the-right-evidence"&gt;Job: make a retrieval-backed AI API answer from the right evidence&lt;/h3&gt;

&lt;p&gt;Use the &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/workshops/from_rag_to_agents_workshop" rel="noopener noreferrer"&gt;From RAG to Agents workshop&lt;/a&gt; when the API must do more than call a model. The workshop builds a research-paper assistant from data loading and retrieval through RAG, agent tools, multi-agent orchestration, and persistent session memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this proves for an API design:&lt;/strong&gt; retrieval and orchestration belong behind a provider-neutral service boundary. Keep &lt;code&gt;POST /v1/predictions&lt;/code&gt; or &lt;code&gt;POST /v1/chat&lt;/code&gt; responsible for validation, identity, status, and response shape; let a worker or agent runtime assemble retrieval, tool calls, and model execution. Use this proof case when the developer job is “turn grounded retrieval into an application feature without exposing the internal pipeline as the public API.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run it now:&lt;/strong&gt; install Docker, Python, and Jupyter; start the workshop’s Oracle AI Database container, install its requirements, and open &lt;code&gt;workshop/notebook_student.ipynb&lt;/code&gt;. The learning path moves from data loading and retrieval through RAG, tools, orchestration, and session memory. The proof is not only that a model answers—it is that you can inspect which retrieval and orchestration boundary produced the answer.&lt;/p&gt;

&lt;h3 id="job-expose-enterprise-data-without-bypassing-identity-and-access-policy"&gt;Job: expose enterprise data without bypassing identity and access policy&lt;/h3&gt;

&lt;p&gt;Use the &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/workshops/enterprise-data-agent-harness-workshop" rel="noopener noreferrer"&gt;Enterprise Data Agent workshop&lt;/a&gt; when the API serves people who should see different data. It pairs a notebook with a running application and demonstrates identity-aware row and column policies, retrieval, memory, tools, and a live chat interface against the same database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this proves for an API design:&lt;/strong&gt; authentication at the route is necessary but not sufficient. Derive the caller identity in FastAPI, carry that identity into the database session through an approved pattern, and make retrieval, conversation reads, and tool access apply the same scope. Use this proof case when the developer job is “add an AI capability to enterprise data without creating a second, ungoverned access path.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run it now:&lt;/strong&gt; use the workshop’s GitHub Codespaces path for the lowest-friction setup, or follow its local Docker, Python, and Node.js setup to start the notebook and companion application. The proof to look for is identity-dependent data access: the same application and agent workflow should be constrained by the active identity and the data policy, rather than trusting an unscoped identifier supplied in a chat request.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Evidence boundary:&lt;/strong&gt; These links are pinned to a specific Developer Hub commit so their code and setup instructions remain reviewable. They demonstrate an API boundary in a working application: Total Recall demonstrates FastAPI and SSE; From RAG to Agents demonstrates retrieval and orchestration; Enterprise Data Agent demonstrates identity-aware access. They do not benchmark the latency, throughput, availability, or security of your deployment. Measure those against your model, data, identity provider, and traffic before release.&lt;/p&gt;&lt;/blockquote&gt;

&lt;h2 id="how-do-you-keep-your-public-api-independent-of-the-ai-provider"&gt;How
do you keep your public API independent of the AI provider?&lt;/h2&gt;

&lt;p&gt;Define a provider-neutral request, result, and error contract at the
application boundary. Put each model SDK behind an adapter that
translates the internal request into the provider call and maps the
result back into your API’s stable schema. Clients should not receive
provider response objects, provider-specific error payloads, or
credentials.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from typing import Protocol

class ModelResult(BaseModel):
    text: str
    provider_request_id: str | None = None

class ModelAdapter(Protocol):
    def generate(self, *, prompt: str, temperature: float) -&amp;gt; ModelResult: ...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Select the adapter through server-side configuration. This lets the
HTTP contract, job schema, authorization, and tests remain stable when
the provider or model changes.&lt;/p&gt;

&lt;h2 id="how-do-fastapi-docs-and-openapi-json-help-during-development"&gt;How do FastAPI &lt;code&gt;/docs&lt;/code&gt; and &lt;code&gt;/openapi.json&lt;/code&gt; help during development?&lt;/h2&gt;

&lt;p&gt;FastAPI generates an interactive &lt;code&gt;/docs&lt;/code&gt; page and a machine-readable &lt;code&gt;/openapi.json&lt;/code&gt; contract from the request and response models. Use them to try authenticated development requests, inspect validation rules, share an exact contract with frontend or platform teams, generate clients, and add contract tests. They improve developer feedback; they do not replace authentication, authorization, rate limits, or gateway controls.&lt;/p&gt;

&lt;p&gt;Before deployment, confirm that &lt;code&gt;/openapi.json&lt;/code&gt; exposes only intentional fields and responses. Decide whether &lt;code&gt;/docs&lt;/code&gt; should remain available outside development, and protect it according to the deployment’s operational policy.&lt;/p&gt;

&lt;h2 id="should-ai-inference-be-synchronous-or-asynchronous"&gt;Should AI
inference be synchronous or asynchronous?&lt;/h2&gt;

&lt;p&gt;Use a synchronous response only when completion time is predictable
and fits the API contract. Use an asynchronous job resource when
inference can take an unpredictable amount of time, requires retries, or
must be audited independently of the HTTP request.&lt;/p&gt;

&lt;p&gt;An AI response may not be ready within a predictable HTTP deadline. A
job resource lets the API acknowledge valid work quickly, prevents a
request thread from waiting on inference, and gives the client a stable
resource to poll.&lt;/p&gt;

&lt;p&gt;Use these resources:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;colgroup&gt;
&lt;col&gt;
&lt;col&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST /v1/predictions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validate a prompt and submit a prediction job. Return
&lt;code&gt;202 Accepted&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /v1/predictions/{id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read the job’s queued, running, succeeded, or failed state.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /healthz&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Let platform health checks confirm that the API process is
available.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Do not make a URL such as &lt;code&gt;/generateText&lt;/code&gt; the primary
interface. The prediction is a resource with a lifecycle; the API should
make that lifecycle visible.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;client → FastAPI → prediction_jobs in Oracle AI Database
                  ↓
             durable worker → approved model adapter
                  ↓
client ← GET /v1/predictions/{id}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;An in-process background task can be useful for a local prototype.
For production, run a separate worker process and use a durable queue or
equivalent platform service so accepted work can survive a web-process
restart.&lt;/p&gt;

&lt;h3 id="how-do-i-add-streaming-responses-for-chat-in-fastapi"&gt;How do I
add streaming responses for chat in FastAPI?&lt;/h3&gt;

&lt;p&gt;Use Server-Sent Events when the client benefits from incremental
response content. Streaming is an output-delivery choice, not a
replacement for durable job state: persist the authoritative result and
expose a status resource even when the user interface receives partial
content.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import asyncio
import json

from fastapi import Request
from fastapi.responses import StreamingResponse

@app.get("/v1/conversations/{conversation_id}/messages/{message_id}/stream")
async def stream_message(request: Request, conversation_id: str, message_id: str):
    async def events():
        try:
            async for chunk in worker.stream(message_id):
                if await request.is_disconnected():
                    return
                yield "event: message\\ndata: " + json.dumps({"delta": chunk}) + "\\n\\n"
            yield "event: done\\ndata: {}\\n\\n"
        except asyncio.CancelledError:
            return
        except Exception:
            yield 'event: error\\ndata: {"code":"stream_failed"}\\n\\n'

    return StreamingResponse(events(), media_type="text/event-stream")&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Define event types and payload shapes as part of the public contract.
Serialize each chunk as JSON rather than interpolating raw model output
into an SSE frame; then check client disconnects, handle cancellation,
keep provider errors server-side, and finalize the durable result for
later retrieval.&lt;/p&gt;

&lt;h2 id="how-do-i-build-an-ai-rest-api-with-fastapi"&gt;How do I build an AI
REST API with FastAPI?&lt;/h2&gt;

&lt;p&gt;Define bounded Pydantic request and response models, reject unknown
fields, use FastAPI dependencies for authentication, return an explicit
status code, and let FastAPI generate the OpenAPI contract from the
implementation.&lt;/p&gt;

&lt;p&gt;The next two snippets show the request model and submission boundary.
Treat them as patterns to adapt to your identity provider, job store,
and worker runtime; the maintained proof cases show the surrounding
application behavior.&lt;/p&gt;

&lt;h3 id="step-1-define-a-bounded-request-schema"&gt;Step 1: Define a bounded
request schema&lt;/h3&gt;

&lt;p&gt;Use Pydantic to reject unknown fields and bound input before the
request reaches an expensive model call.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class PredictionRequest(BaseModel):
    model_config = ConfigDict(extra="forbid")

    prompt: Annotated[str, Field(min_length=5, max_length=8_000)]
    temperature: Annotated[float, Field(default=0.2, ge=0.0, le=1.0)]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The limits are application decisions, not universal defaults. Set
them from the model context window, your abuse policy, and your expected
payload shape.&lt;/p&gt;

&lt;h3 id="step-2-submit-work-and-return-202-accepted"&gt;Step 2: Submit work
and return &lt;code&gt;202 Accepted&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The endpoint creates a prediction job before it schedules work. It
returns a &lt;code&gt;Location&lt;/code&gt; header and a JSON status URL.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from fastapi import BackgroundTasks, Depends, Header, Request, status
from fastapi.responses import JSONResponse

@app.post("/v1/predictions")
async def create_prediction(
    payload: PredictionRequest,
    background_tasks: BackgroundTasks,
    request: Request,
    identity: VerifiedIdentity = Depends(require_identity),
    idempotency_key: str | None = Header(default=None, alias="Idempotency-Key"),
):
    job_id = str(uuid4())
    status_url = f"/v1/predictions/{job_id}"
    repository.create(
        job_id=job_id,
        tenant_id=identity.tenant_id,
        client_id=identity.user_id,
        prompt=payload.prompt,
        temperature=payload.temperature,
        request_id=request.state.request_id,
        idempotency_key=idempotency_key,
    )
    background_tasks.add_task(worker.process_once)
    return JSONResponse(
        status_code=status.HTTP_202_ACCEPTED,
        headers={"Location": status_url},
        content={"id": job_id, "status": "queued", "status_url": status_url},
    )&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Use &lt;code&gt;202 Accepted&lt;/code&gt; when the API has accepted work but has
not completed it. Do not choose a fixed time threshold such as two
seconds; use a job resource whenever the result cannot be completed
reliably within the request contract.&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;BackgroundTasks&lt;/code&gt; example is illustrative, not a
production runtime pattern. For work that is heavy, retried, or must
survive a process restart, use a separate worker and durable queue;
FastAPI makes the same distinction in its &lt;a href="https://fastapi.tiangolo.com/tutorial/background-tasks/" rel="noopener noreferrer"&gt;background-task guidance&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="how-would-i-persist-ai-jobs-with-oracle-ai-database-in-production"&gt;How would I
persist AI jobs with Oracle AI Database in production?&lt;/h2&gt;

&lt;p&gt;Store the job request, tenant scope, lifecycle status, safe result or
error code, request correlation ID, and idempotency fingerprint in
Oracle AI Database. Keep transactions short and use bind variables
through a connection pool.&lt;/p&gt;

&lt;h3 id="step-3-persist-prediction-status-in-oracle-ai-database"&gt;Step 3:
Persist prediction status in Oracle AI Database&lt;/h3&gt;

&lt;p&gt;The service records the request, client scope, status, result, safe
error code, and request ID in a &lt;code&gt;prediction_jobs&lt;/code&gt; table. This
keeps API state close to the application data that may shape the model
request or govern who can retrieve the result.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CREATE TABLE prediction_jobs (
    id             VARCHAR2(36) PRIMARY KEY,
    tenant_id      VARCHAR2(255) NOT NULL,
    client_id      VARCHAR2(255) NOT NULL,
    status         VARCHAR2(16) NOT NULL,
    prompt         CLOB NOT NULL,
    temperature    NUMBER(3,2) NOT NULL,
    generated_text CLOB,
    error_code     VARCHAR2(128),
    request_id     VARCHAR2(64) NOT NULL,
    idempotency_key VARCHAR2(255),
    request_fingerprint VARCHAR2(64),
    created_at     TIMESTAMP WITH TIME ZONE NOT NULL,
    updated_at     TIMESTAMP WITH TIME ZONE NOT NULL,
    CONSTRAINT prediction_jobs_idempotency_uq
      UNIQUE (tenant_id, idempotency_key)
);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Use bind variables for database calls. A production implementation
should use &lt;code&gt;oracledb.create_pool&lt;/code&gt;, then acquire a connection
only for the short transaction that creates, claims, or completes a
job.&lt;/p&gt;

&lt;h3 id="step-4-let-a-worker-handle-model-inference"&gt;Step 4: Let a worker
handle model inference&lt;/h3&gt;

&lt;p&gt;The worker claims one queued job, calls a model adapter, and records
either the result or a safe error code.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def process_once(self) -&amp;gt; bool:
    job = self._repository.claim_next()
    if not job:
        return False
    try:
        result = self._model.generate(prompt=job.prompt, temperature=job.temperature)
        self._repository.mark_succeeded(job.id, result)
    except Exception:
        self._repository.mark_failed(job.id, "model_inference_failed")
    return True&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Keep raw provider errors out of public responses. Log only redacted,
correlated diagnostics, then use &lt;code&gt;request_id&lt;/code&gt; to investigate
the failure internally.&lt;/p&gt;

&lt;h2 id="how-do-i-build-a-minimal-fastapi-chat-endpoint-using-the-openai-sdk"&gt;How do I build a minimal FastAPI chat endpoint using the OpenAI SDK?&lt;/h2&gt;

&lt;p&gt;For a minimal, synchronous prototype, validate the message, call the OpenAI SDK from the server, and return your own stable JSON shape. Keep this route small: it demonstrates the request boundary, not a durable production job system.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import os
from fastapi import FastAPI
from openai import AsyncOpenAI
from pydantic import BaseModel, Field

app = FastAPI()
client = AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"])

class ChatRequest(BaseModel):
    message: str = Field(min_length=1, max_length=8_000)

@app.post("/v1/chat")
async def create_chat(payload: ChatRequest):
    response = await client.responses.create(
        model=os.environ["OPENAI_MODEL"],
        input=payload.message,
    )
    return {"output_text": response.output_text}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For variable, costly, or auditable inference, replace the direct SDK call with the &lt;code&gt;202&lt;/code&gt; prediction-job pattern described above. Add the authenticated &lt;code&gt;Depends&lt;/code&gt; pattern in the security section before using this route outside a local prototype. Do not return the provider’s raw response object or expose provider credentials to the client.&lt;/p&gt;

&lt;h3 id="how-do-i-connect-a-rest-api-to-the-openai-responses-api"&gt;How do I connect a REST API to the OpenAI Responses API?&lt;/h3&gt;

&lt;p&gt;Keep the model provider behind a server-side adapter. Load &lt;code&gt;OPENAI_API_KEY&lt;/code&gt; from a secret manager or environment, set &lt;code&gt;OPENAI_MODEL&lt;/code&gt; explicitly, call the Responses API from the worker, capture the provider request ID for diagnostics, and return only your API’s stable response contract.&lt;/p&gt;

&lt;h3 id="how-should-you-store-and-load-the-openai-api-key-securely"&gt;How
should you store and load the OpenAI API key securely?&lt;/h3&gt;

&lt;p&gt;Store the OpenAI API key in an approved secret manager and inject it
into the server or worker at runtime. Environment variables are suitable
as the process-level delivery mechanism, but the value should originate
from managed secret storage rather than source code, a Docker image, a
notebook, browser JavaScript, or a committed &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a different secret for each environment and workload
identity.&lt;/li&gt;
&lt;li&gt;Restrict who and what can read the secret.&lt;/li&gt;
&lt;li&gt;Fail startup when the required secret is missing instead of
accepting it from a client request.&lt;/li&gt;
&lt;li&gt;Do not print the key in logs, traces, exceptions, health responses,
or configuration dumps.&lt;/li&gt;
&lt;li&gt;Rotate the key according to the organization’s credential policy and
after suspected exposure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Load the value server-side and pass it directly to the provider
client:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import os
from openai import OpenAI

api_key = os.environ["OPENAI_API_KEY"]
openai_client = OpenAI(api_key=api_key)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is an illustrative provider boundary, not a downloadable
implementation or configuration recipe. The current &lt;a href="https://developers.openai.com/api/reference/overview" rel="noopener noreferrer"&gt;OpenAI API
reference&lt;/a&gt; describes Responses as the direct model-request API,
requires server-side bearer credentials, and recommends logging request
IDs and reviewing rate limits before production.&lt;/p&gt;

&lt;p&gt;Do not put the provider key in browser JavaScript or accept it from
the request body. Return a stable internal error instead of a raw
provider error. The API’s caller authentication and the model provider’s
authentication are separate trust boundaries.&lt;/p&gt;

&lt;h2 id="how-do-i-authenticate-and-secure-an-ai-rest-api"&gt;How do I
authenticate and secure an AI REST API?&lt;/h2&gt;

&lt;p&gt;Authenticate before expensive work, resolve tenant identity from
verified credentials, apply authorization to every job read and update,
enforce request limits, bound payloads, support idempotent retries,
redact provider errors, and define retention for prompts and generated
responses.&lt;/p&gt;

&lt;h3 id="how-do-you-store-conversations-and-enforce-user-ownership-checks"&gt;How
do you store conversations and enforce user ownership checks?&lt;/h3&gt;

&lt;p&gt;Store each conversation with a server-derived &lt;code&gt;tenant_id&lt;/code&gt;
and &lt;code&gt;owner_id&lt;/code&gt;, then carry the conversation identifier into
its messages and prediction jobs. Every read, update, stream, and delete
operation must include the authenticated ownership scope; knowing a
conversation ID is not authorization.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SELECT conversation_id, status, updated_at
FROM ai_conversations
WHERE conversation_id = :conversation_id
  AND tenant_id = :authenticated_tenant_id
  AND owner_id = :authenticated_user_id;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Apply the same scope when loading message history or returning
generated content. For defense in depth, enforce the tenant predicate in
Oracle AI Database with an approved policy such as VPD, and test that
one user cannot read, stream, update, or delete another user’s
conversation.&lt;/p&gt;

&lt;p&gt;For chatbot-style endpoints, store conversations and messages with an
owner or tenant identifier and enforce that ownership on every endpoint
that returns context, history, or generated content. Also account for
prompt injection, unsafe generated output, unintended tool calls,
sensitive-data disclosure, and cost exhaustion. Mitigate these risks
with strict schema validation, tool allowlists, least-privilege data
access, timeouts, token budgets, redaction, and human approval for
consequential actions.&lt;/p&gt;

&lt;h3 id="how-do-i-add-user-authentication-with-fastapi-depends"&gt;How do I
add user authentication with FastAPI &lt;code&gt;Depends&lt;/code&gt;?&lt;/h3&gt;

&lt;p&gt;Inject a verified identity object before creating an inference job or
calling a provider. Derive &lt;code&gt;tenant_id&lt;/code&gt; and
&lt;code&gt;user_id&lt;/code&gt; from a validated JWT, OAuth session, or equivalent
credential; do not accept them as authoritative request-body fields.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from fastapi import Depends, HTTPException, status
from pydantic import BaseModel

class VerifiedIdentity(BaseModel):
    tenant_id: str
    user_id: str

async def require_identity() -&amp;gt; VerifiedIdentity:
    identity = await verify_request_credentials()
    if identity is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
    return identity

@app.post("/v1/chat")
async def chat(request: ChatRequest,
               identity: VerifiedIdentity = Depends(require_identity)):
    # Scope reads and writes with identity.tenant_id and identity.user_id.
    return await create_scoped_job(request, identity)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This dependency is illustrative. Replace
&lt;code&gt;verify_request_credentials()&lt;/code&gt; with the approved
authentication mechanism for the deployment and enforce the same
identity in the database policy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication and authorization:&lt;/strong&gt; Require
authenticated callers and scope every job to a tenant or user. An opaque
prediction ID is not authorization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting:&lt;/strong&gt; A process-local guard is suitable
only for a demonstration. Enforce distributed limits with an API
gateway, Redis-backed service, or equivalent platform control in
multi-instance deployments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input controls:&lt;/strong&gt; Bound prompt length, reject unknown
JSON fields, and validate media uploads separately from text
requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency:&lt;/strong&gt; Add an idempotency key for clients
that may retry &lt;code&gt;POST&lt;/code&gt; after network failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability:&lt;/strong&gt; Return a request ID, record the job
ID and tenant-safe correlation data, and measure queue delay, inference
duration, errors, and abandonment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retention:&lt;/strong&gt; Define when prompts and generated
responses expire or must be deleted; do not retain them indefinitely by
default.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="oracle-ai-database-pattern-tenant-scope-and-request-correlation"&gt;Oracle
AI Database pattern: tenant scope and request correlation&lt;/h3&gt;

&lt;p&gt;The API must authenticate the caller before it opens a database
session. The following is &lt;strong&gt;simplified Oracle pseudocode&lt;/strong&gt;,
based on &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg/using-oracle-vpd-to-control-data-access.html" rel="noopener noreferrer"&gt;application
contexts and Oracle Virtual Private Database (VPD)&lt;/a&gt; and &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/DBMS_APPLICATION_INFO.html" rel="noopener noreferrer"&gt;&lt;code&gt;DBMS_APPLICATION_INFO&lt;/code&gt;&lt;/a&gt;.
It is not a copy-and-paste production policy: configure a trusted
context-setting package and review the VPD licensing and security
requirements for the target deployment. &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/DBMS_RLS.html" rel="noopener noreferrer"&gt;&lt;code&gt;DBMS_RLS&lt;/code&gt;&lt;/a&gt;, which implements VPD, is available with Oracle AI Database Enterprise Edition only; confirm the edition and deployment controls before adopting this pattern.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- The API derives these values from authenticated identity; do not accept a
-- tenant ID or client identity as an untrusted request-body field.
BEGIN
  trusted_ai_api_context.set_identity(
    tenant_id  =&amp;gt; :authenticated_tenant_id,
    client_id  =&amp;gt; :authenticated_client_id
  );

  -- Safe correlation only: do not place prompts, tokens, or secrets here.
  DBMS_APPLICATION_INFO.SET_MODULE(
    module_name =&amp;gt; 'ai_predictions_api',
    action_name =&amp;gt; 'create_prediction'
  );
  DBMS_APPLICATION_INFO.SET_CLIENT_INFO(:request_id);
END;
/

-- A VPD policy function can add this predicate whenever PREDICTION_JOBS is
-- queried or changed. The application query stays simple; the database
-- enforces the current tenant scope.
CREATE OR REPLACE FUNCTION prediction_tenant_predicate(
  object_schema VARCHAR2,
  object_name   VARCHAR2
) RETURN VARCHAR2 AS
BEGIN
  RETURN q'[tenant_id = SYS_CONTEXT('AI_API_CTX', 'TENANT_ID')]';
END;
/

BEGIN
  DBMS_RLS.ADD_POLICY(
    object_schema   =&amp;gt; 'APP_SCHEMA', -- replace with the schema that owns PREDICTION_JOBS
    object_name     =&amp;gt; 'PREDICTION_JOBS',
    policy_name     =&amp;gt; 'PREDICTION_TENANT_SCOPE',
    policy_function =&amp;gt; 'PREDICTION_TENANT_PREDICATE',
    statement_types =&amp;gt; 'SELECT,INSERT,UPDATE,DELETE',
    update_check    =&amp;gt; TRUE
  );
END;
/

-- Idempotency belongs in durable state, not only in the API process.
-- The PREDICTION_JOBS table definition above already enforces it with
-- UNIQUE (tenant_id, idempotency_key); do not add the constraint twice.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Use &lt;code&gt;DBMS_APPLICATION_INFO&lt;/code&gt; to correlate a safe request ID
with database work; its &lt;code&gt;CLIENT_INFO&lt;/code&gt; field is not a location
for secret or sensitive prompt data. For systems that use VPD, the
policy function is attached to the protected table and Oracle applies
the predicate to the configured statement types. Treat this entire SQL
section as illustrative architecture guidance and have it reviewed for
the target edition, schema design, session-pooling behavior, and identity
model.&lt;/p&gt;

&lt;h2 id="when-should-i-use-fastapi-ords-streaming-or-a-queue"&gt;When
should I use FastAPI, ORDS, streaming, or a queue?&lt;/h2&gt;

&lt;p&gt;Use FastAPI for application-facing validation and model
orchestration, ORDS for a governed database-owned resource, SSE for
incremental interactive output, and a durable queue for production
worker delivery. These choices can coexist, but they solve different
boundaries.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;colgroup&gt;
&lt;col&gt;
&lt;col&gt;
&lt;col&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Best-fit boundary&lt;/th&gt;
&lt;th&gt;Example or resource&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Short predictable inference&lt;/td&gt;
&lt;td&gt;Synchronous REST response&lt;/td&gt;
&lt;td&gt;Direct route with bounded input and timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-running inference&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;202&lt;/code&gt; job resource plus worker&lt;/td&gt;
&lt;td&gt;Job-resource and worker pattern described above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token-by-token interaction&lt;/td&gt;
&lt;td&gt;SSE or another streaming protocol&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/apps/appbooks/agent_harness_palo_stack" rel="noopener noreferrer"&gt;Total
Recall appbook&lt;/a&gt;, as an advanced path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database-owned status resource&lt;/td&gt;
&lt;td&gt;ORDS&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/" rel="noopener noreferrer"&gt;Oracle
REST Data Services documentation&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrieval-backed application&lt;/td&gt;
&lt;td&gt;Separate retrieval API&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/workshops/from_rag_to_agents_workshop" rel="noopener noreferrer"&gt;From
RAG to Agents workshop&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ORDS is useful when a database-owned resource needs a governed REST
interface. It does not need to replace FastAPI as the public API
boundary for model orchestration. In this pattern, FastAPI owns request
validation and model lifecycle; ORDS can provide a narrowly scoped,
read-only integration for job-status data when that is useful to an
existing database-facing application.&lt;/p&gt;

&lt;h2 id="how-do-i-test-and-deploy-an-ai-rest-api"&gt;How do I test and
deploy an AI REST API?&lt;/h2&gt;

&lt;p&gt;Test the public contract, authentication, tenant isolation,
idempotency, rate limiting, worker transitions, provider failures, and
OpenAPI output before deployment. Run the worker separately in
production and configure provider and database credentials outside
source control.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with the existing &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/apps/appbooks/agent_harness_palo_stack" rel="noopener noreferrer"&gt;Total
Recall appbook&lt;/a&gt; for the FastAPI/SSE boundary, then use the &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/workshops/from_rag_to_agents_workshop" rel="noopener noreferrer"&gt;From
RAG to Agents workshop&lt;/a&gt; for database-backed orchestration
patterns.&lt;/li&gt;
&lt;li&gt;Implement the resource contract with bounded Pydantic schemas and an
authenticated identity dependency.&lt;/li&gt;
&lt;li&gt;Add durable job state, tenant scope, idempotency, and a worker
before exposing a provider call.&lt;/li&gt;
&lt;li&gt;Add Oracle AI Database persistence and connection pooling through an
approved repository implementation.&lt;/li&gt;
&lt;li&gt;Run contract, authentication, tenant-isolation, streaming, worker,
and provider-adapter tests before deployment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The OpenAPI contract should be available at
&lt;code&gt;/openapi.json&lt;/code&gt;. Use it to generate client SDKs, run contract
tests, and review request/response schemas during integration. During
development, FastAPI’s &lt;code&gt;/docs&lt;/code&gt; page can help inspect
endpoints and validated models, but neither endpoint is a security
control: authorization, tenant scoping, and rate limiting must still be
enforced in API logic and gateway policies.&lt;/p&gt;

&lt;h3 id="troubleshooting-an-ai-rest-api"&gt;Troubleshooting an AI REST API&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;401 unauthorized&lt;/code&gt;:&lt;/strong&gt; Verify that the API derives identity from approved credentials and that the client presents the required credential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;422 Unprocessable Entity&lt;/code&gt;:&lt;/strong&gt; Check the
prompt length, temperature range, JSON field names, and
&lt;code&gt;Content-Type: application/json&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;409 idempotency_conflict&lt;/code&gt;:&lt;/strong&gt; Reuse an
&lt;code&gt;Idempotency-Key&lt;/code&gt; only for the same logical request body;
generate a new key for changed work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;404 not_found&lt;/code&gt; while polling:&lt;/strong&gt; Scope the status lookup to the same authenticated tenant that created the job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Work disappears after a restart:&lt;/strong&gt; Move accepted work into durable state and process it through a separate worker or queue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/docs&lt;/code&gt; loads but requests fail:&lt;/strong&gt; OpenAPI
describes the contract; it does not supply authentication or
configuration. Inspect the request credential and JSON error code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="production-checklist"&gt;Production checklist&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Validate and reject unknown request
fields.&lt;/li&gt;
&lt;li&gt;Authenticate before creating a job
or calling a provider.&lt;/li&gt;
&lt;li&gt;Scope every status read by
authenticated tenant identity.&lt;/li&gt;
&lt;li&gt;Support &lt;code&gt;Idempotency-Key&lt;/code&gt;
for safe client retries.&lt;/li&gt;
&lt;li&gt;Use a durable worker and queue for
production inference.&lt;/li&gt;
&lt;li&gt;Record request, queue, inference,
and provider correlation IDs without logging prompts or
secrets.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;OPENAI_MODEL&lt;/code&gt; or the
equivalent provider model explicitly.&lt;/li&gt;
&lt;li&gt;Define retention and deletion
behavior for prompts and outputs.&lt;/li&gt;
&lt;li&gt;Run contract and failure-path tests
before publishing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="faq"&gt;FAQ&lt;/h2&gt;

&lt;h3 id="how-do-i-build-a-rest-api-for-an-ai-application"&gt;How do I build
a REST API for an AI application?&lt;/h3&gt;

&lt;p&gt;Use FastAPI to validate and authenticate requests, persist an
inference job, return &lt;code&gt;202 Accepted&lt;/code&gt; with a status URL, and
let a worker call the model provider. Store job state in a durable
database such as Oracle AI Database, scope reads to the authenticated
tenant, and return stable JSON status and error responses.&lt;/p&gt;

&lt;h3 id="what-are-the-best-practices-for-an-ai-rest-api"&gt;What are the
best practices for an AI REST API?&lt;/h3&gt;

&lt;p&gt;The core practices are bounded input validation, server-side provider
authentication, rate limiting, durable asynchronous jobs, tenant-aware
authorization, idempotency, structured errors, request correlation,
retention controls, and tests for failure paths—not only successful
inference.&lt;/p&gt;

&lt;h3 id="how-do-i-authenticate-an-openai-powered-rest-api"&gt;How do I
authenticate an OpenAI-powered REST API?&lt;/h3&gt;

&lt;p&gt;Authenticate the API caller at your service boundary, then
authenticate separately with the model provider from the server or
worker. Keep the provider key in a secret manager or protected
environment variable, set the model explicitly, capture a safe provider
request ID, and do not send the key or raw provider error to the
client.&lt;/p&gt;

&lt;h3 id="should-i-use-fastapi-or-ords-for-an-ai-application-api"&gt;Should I
use FastAPI or ORDS for an AI application API?&lt;/h3&gt;

&lt;p&gt;Use FastAPI for application-facing validation, authentication,
orchestration, and model lifecycle management. Use ORDS when a
database-owned resource needs a governed REST interface. They can
coexist; ORDS does not need to replace FastAPI as the public
model-orchestration boundary.&lt;/p&gt;

&lt;h3 id="should-an-ai-rest-api-return-a-result-synchronously"&gt;Should an
AI REST API return a result synchronously?&lt;/h3&gt;

&lt;p&gt;It can for predictable, short operations. Use a job resource and
&lt;code&gt;202 Accepted&lt;/code&gt; when an operation may not finish reliably in
the request window or when you need durable retry, audit, and status
behavior.&lt;/p&gt;

&lt;h3 id="is-fastapi-enough-for-a-production-ai-application"&gt;Is FastAPI
enough for a production AI application?&lt;/h3&gt;

&lt;p&gt;FastAPI is a useful HTTP framework, but it is only one layer.
Production behavior also needs durable persistence, a worker or queue,
authentication, limits, observability, and retention controls.&lt;/p&gt;

&lt;h3 id="which-maintained-proof-case-should-i-run-for-an-ai-rest-api"&gt;Which maintained proof case should I run for an AI REST API?&lt;/h3&gt;

&lt;p&gt;Run &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/apps/appbooks/agent_harness_palo_stack" rel="noopener noreferrer"&gt;Total Recall&lt;/a&gt; for FastAPI and SSE, the &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/workshops/from_rag_to_agents_workshop" rel="noopener noreferrer"&gt;From RAG to Agents workshop&lt;/a&gt; for retrieval and orchestration, or the &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/workshops/enterprise-data-agent-harness-workshop" rel="noopener noreferrer"&gt;Enterprise Data Agent workshop&lt;/a&gt; for identity-aware, governed data access. Choose the proof case that matches the boundary you need to validate first.&lt;/p&gt;

&lt;h3 id="how-does-oracle-ai-database-help-with-an-ai-api"&gt;How does Oracle
AI Database help with an AI API?&lt;/h3&gt;

&lt;p&gt;It can persist job state and application data in the same governed
data platform. The application API still validates and orchestrates
requests; Oracle AI Database provides the durable state and data-access
layer behind it.&lt;/p&gt;

&lt;h2 id="next-steps"&gt;Next steps&lt;/h2&gt;

&lt;p&gt;Choose one maintained proof case, then apply the API contract in this guide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/apps/appbooks/agent_harness_palo_stack" rel="noopener noreferrer"&gt;Total Recall&lt;/a&gt; for FastAPI, SSE, traces, and the agent harness boundary.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/workshops/from_rag_to_agents_workshop" rel="noopener noreferrer"&gt;From RAG to Agents&lt;/a&gt; for retrieval, tools, orchestration, and session memory.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/74f7b01b3d85c1bfc94a66c72011da55fd316d61/workshops/enterprise-data-agent-harness-workshop" rel="noopener noreferrer"&gt;Enterprise Data Agent&lt;/a&gt; for identity-aware, governed access to enterprise data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="documentation"&gt;Documentation&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/" rel="noopener noreferrer"&gt;Oracle
REST Data Services documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://python-oracledb.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;python-oracledb
documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fastapi.tiangolo.com/tutorial/body/" rel="noopener noreferrer"&gt;FastAPI
request-body documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fastapi.tiangolo.com/tutorial/security/" rel="noopener noreferrer"&gt;FastAPI
security documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://owasp.org/API-Security/editions/2023/en/0x11-t10/" rel="noopener noreferrer"&gt;OWASP
API Security Top 10&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.openai.com/api/reference/overview" rel="noopener noreferrer"&gt;OpenAI API
reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>ai</category>
      <category>oracle</category>
      <category>restapi</category>
    </item>
    <item>
      <title>How to Build a Controlled MCP Workflow for Codex and Oracle AI Database</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Wed, 12 Aug 2026 15:28:59 +0000</pubDate>
      <link>https://dev.to/oracledevs/how-to-build-a-controlled-mcp-workflow-for-codex-and-oracle-ai-database-5e90</link>
      <guid>https://dev.to/oracledevs/how-to-build-a-controlled-mcp-workflow-for-codex-and-oracle-ai-database-5e90</guid>
      <description>&lt;p&gt;&lt;strong&gt;Connect Codex CLI to Oracle AI Database through&amp;nbsp;SQLcl&amp;nbsp;MCP, then Add Oracle AI agent memory and&amp;nbsp;LangChain&amp;nbsp;Retrieval.&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Companion notebook:&lt;/strong&gt;&amp;nbsp;&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/codex_mcp_oracle_ai_database_memory_langchain.ipynb" rel="noopener noreferrer"&gt;Codex MCP Oracle AI Database&lt;/a&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Key Takeaways&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro" rel="noopener noreferrer"&gt;MCP&lt;/a&gt; turns AI-to-database access into an explicit tool contract instead of implicit system access. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.4/sqcug/using-oracle-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;Oracle SQLcl in MCP&lt;/a&gt; mode (sql -mcp) is a direct, documented way to connect Codex CLI to Oracle AI Database through an MCP server. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Oracle AI Database provides the persistent storage and vector search layer for memory workloads, while &lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/agmea/get-started.html" rel="noopener noreferrer"&gt;Oracle AI Agent Memory&lt;/a&gt; gives teams a Python API for threads, durable memory records, scoped retrieval, and context assembly on top of it.&amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://docs.langchain.com/oss/python/integrations/vectorstores/oracle" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt; can provide application-side wrappers and orchestration after the Oracle-backed memory and retrieval path is in place.&lt;/li&gt;



&lt;li&gt;A strong default is hybrid: Codex CLI plus MCP for interactive database work, Oracle AI Database plus Oracle AI Agent Memory for durable memory, and&amp;nbsp;LangChain&amp;nbsp;only when the application needs reusable&amp;nbsp;retrieval&amp;nbsp;orchestration.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Here is how those components connect in this pattern. Codex works with Oracle through SQLcl MCP’s explicit tools. The saved SQLcl profile, database user, grants, and database policies remain the enforcement boundary. Oracle AI Agent Memory is the Python package your application uses to manage memory records on Oracle AI Database. LangChain is optional application-side orchestration after the database-backed memory and evidence path is in place; it is not the permission boundary or source of truth.&lt;/p&gt;

&lt;p&gt;Production success depends less on clever prompting and more on boundaries, privileges, logging, scoped retrieval, and repeatable runbooks.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;This guide is for&amp;nbsp;developers&amp;nbsp;who&amp;nbsp;want Codex CLI to work with Oracle AI Database through explicit tools, durable memory, and reviewable retrieval evidence.&amp;nbsp;It also shows an AI agent memory architecture where Codex handles the&amp;nbsp;developer&amp;nbsp;interaction,&amp;nbsp;SQLcl MCP provides the tool boundary for database access; the saved SQLcl profile and database user permissions determine what Codex can actually do, and Oracle AI Database stores durable memory and retrieval evidence.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The developer path through this guide is simple:&lt;/strong&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with&amp;nbsp;one&amp;nbsp;approved Oracle connection and a read-only validation query. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Put&amp;nbsp;SQLcl&amp;nbsp;MCP in front of that&amp;nbsp;connection&amp;nbsp;so Codex sees tools, not raw database credentials. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Check the audit and activity trail before adding more tool access. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Add Oracle AI Agent Memory when the workflow needs durable thread context, scoped recall, or reusable context cards. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Add&amp;nbsp;LangChain&amp;nbsp;only when you need application-side&amp;nbsp;retrieval&amp;nbsp;orchestration beyond the MCP interaction loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F07%2FImage-2-3-1024x391.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F07%2FImage-2-3-1024x391.png" alt="Architecture diagram showing a human operator using Codex CLI through a controlled SQLcl MCP tool path to Oracle AI Database and Oracle AI Agent Memory. Agent Memory connects to memory tables, tool logs, and vector retrieval. A separate LangChain retrieval path queries the database and returns grounded context to Codex CLI, forming the memory and retrieval loop." width="799" height="305"&gt;&lt;/a&gt;Controlled&amp;nbsp;Codex MCP + Oracle AI Database Workflow&amp;nbsp;&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Why This&amp;nbsp;Architecture&amp;nbsp;Is&amp;nbsp;Useful&amp;nbsp;for&amp;nbsp;Developers&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Giving an AI assistant database access is easy.&amp;nbsp;But making&amp;nbsp;that access&amp;nbsp;controlled, inspectable, and repeatable is the hard part.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Codex is useful because it sits close to the developer’s actual work: files, commands, notebooks, configuration, and implementation details. That closeness becomes more sensitive when the workflow touches a database.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The question is not whether Codex can produce SQL-shaped text. The question is whether the database path is approved, observable, and easy to debug later. A useful workflow needs to preserve the request, the tool call, the database identity, the retrieved context, and the reason a risky action was allowed or blocked.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;By the end of this guide, you should know how to connect Codex CLI to Oracle AI Database through a controlled MCP boundary, when Codex context is enough and when Oracle-backed memory is needed, and how to build a retrieval path that can be queried, audited, and scaled.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The workflow has four layers. Codex CLI is the developer-facing agent interface.&amp;nbsp;SQLcl&amp;nbsp;MCP is the tool boundary. Oracle AI Database is the durable substrate for memory, traces, and retrieval. The notebook is the build-and-validation harness that proves the pieces are wired correctly before the workflow is handed to Codex.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;That separation matters. Codex does not need raw credentials in its prompt.&amp;nbsp;SQLcl&amp;nbsp;MCP can be configured once using the saved alias. Oracle AI Database&amp;nbsp;owns&amp;nbsp;persistence and retrieval. The notebook shows the setup in a way that can be rerun, inspected, and debugged.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;A simple way&amp;nbsp;to divide the responsibilities:&amp;nbsp;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Layer&lt;/td&gt;
&lt;td&gt;Responsibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLcl MCP&lt;/td&gt;
&lt;td&gt;Exposes declared Oracle tools to Codex; it is the tool boundary.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle AI Database&lt;/td&gt;
&lt;td&gt;Stores durable data, retrieval evidence, vectors, metadata, traces, and enforces database privileges.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle AI Agent Memory&lt;/td&gt;
&lt;td&gt;Provides application APIs for users, agents, threads, durable memories, scoped retrieval, and context assembly.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LangChain&lt;/td&gt;
&lt;td&gt;Wraps Oracle-backed retrieval results as&amp;nbsp;&lt;code&gt;Document&lt;/code&gt;&amp;nbsp;objects and supports application-side orchestration.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;





&lt;h2&gt;
&lt;strong&gt;The Two&amp;nbsp;Execution&amp;nbsp;Loops&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Building on the separation of responsibilities above, the system naturally forms two execution loops:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loop A is the operational interaction loop: Codex CLI works with MCP to discover tools, inspect data, run bounded read-only queries, and return results&amp;nbsp;immediately.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Loop B is the durable memory and retrieval loop: application code writes history, tool logs, memory records, chunks, and embeddings to Oracle AI Database, then retrieves context before a later answer or workflow step.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F07%2FImage-1-3-1024x415.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F07%2FImage-1-3-1024x415.png" alt="Diagram showing two linked loops. Loop A handles real-time interaction from user through Codex CLI, SQLcl MCP, Oracle AI Database, and answer. Loop B records tool traces into a memory store and Oracle AI Agent Memory, then uses hybrid retrieval to produce grounded context. Retrieved context returns to the agent for later interactions." width="799" height="324"&gt;&lt;/a&gt;Dual Execution Loop: MCP Interaction and Durable Memory &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;SQLcl&amp;nbsp;MCP is for Codex&amp;nbsp;operating&amp;nbsp;interactively through declared tools. Oracle AI Agent Memory is for application code that stores turns, retrieves history, and assembles context before Codex or another assistant sees a prompt. You can drop either one for a narrower use case, but most production setups need both loops.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Reproducing the&amp;nbsp;SQLcl&amp;nbsp;MCP and Codex CLI Workflow&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;The setup should be&amp;nbsp;reproducible.&amp;nbsp;SQLcl&amp;nbsp;runs in MCP mode with&amp;nbsp;sql&amp;nbsp;-mcp. Codex CLI launches it as an MCP server and talks to Oracle through declared tools, not through direct access. Connections come from saved&amp;nbsp;SQLcl&amp;nbsp;profiles that you create and test before Codex uses them.&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Codex does not invent them at runtime; it reuses profiles you have already created and&amp;nbsp;validated.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites before you connect Codex CLI:&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oracle&amp;nbsp;SQLcl&amp;nbsp;25.2.0 or higher. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Oracle JRE 17 or 21. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Codex CLI&amp;nbsp;or another MCP-capable client you are explicitly configuring and testing. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;At least one saved&amp;nbsp;SQLcl&amp;nbsp;connection profile under&amp;nbsp;&lt;em&gt;~/.dbtools&lt;/em&gt;, created with password persistence for MCP use. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;A database user with the minimum permissions&amp;nbsp;required&amp;nbsp;for the workflow.&amp;nbsp;Start with read-only access and a sanitized development or replica environment where possible. &amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The notebook deliberately treats the saved&amp;nbsp;SQLcl&amp;nbsp;connection alias as a first-class artifact. In local development, that alias is what lets&amp;nbsp;SQLcl&amp;nbsp;MCP connect without forcing the agent to assemble credentials dynamically.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The notebook then generates a sanitized Codex MCP config preview.&amp;nbsp;The preview is intentionally safe: it shows the server command and arguments without exposing secrets.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;For the saved connection itself, the important detail is &lt;code&gt;-savepwd&lt;/code&gt;. MCP cannot stop and ask a human for the password every time Codex invokes a database tool. The saved alias becomes the repeatable local path Codex can use after you have reviewed it.&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

```toml 
[mcp_servers.sqlcl]
command = "/absolute/path/to/sql"
args = ["-mcp"]
```

 &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That small TOML block defines the connection between Codex and&amp;nbsp;SQLcl&amp;nbsp;MCP Server. Codex interacts with the database through the tools and permissions exposed by the MCP server, using the saved&amp;nbsp;SQLcl&amp;nbsp;connection profile you created and tested first.&amp;nbsp;Save this configuration in&amp;nbsp;&lt;code&gt;~/.codex/config.toml&lt;/code&gt;, restart Codex, then run&amp;nbsp;&lt;code&gt;codex mcp list&lt;/code&gt;&amp;nbsp;to confirm that the SQLcl MCP server and its tools are discoverable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validation checklist before expanding access&lt;/strong&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run&amp;nbsp;&lt;em&gt;sql&amp;nbsp;-mcp&lt;/em&gt;&amp;nbsp;locally and confirm the server starts. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Restart Codex CLI and confirm the&amp;nbsp;SQLcl&amp;nbsp;tools are discoverable. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Run one read-only query against an approved schema. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Check database-side MCP activity logs and session metadata. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Document the connection alias, database user, grant scope, restrict level, and troubleshooting owner. &amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Good first proof looks like this:&lt;/strong&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The MCP server starts without a Java or path error. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Codex lists the&amp;nbsp;SQLcl&amp;nbsp;MCP tools after&amp;nbsp;restart. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;A read-only query succeeds against the expected schema. &amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;The notebook audit trail records the expected tool interaction in &lt;code&gt;codex_tool_logs&lt;/code&gt;.&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;For live Codex CLI +&amp;nbsp;SQLcl&amp;nbsp;MCP validation, confirm the database/session activity through your normal Oracle monitoring path.&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;A denied query fails because of the database role, not because a prompt asked nicely.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful MCP boundary is more than&amp;nbsp;tool&amp;nbsp;discovery. It is an operating contract. The notebook models that contract with risk levels, read-only defaults, confirmation requirements, scope checks, and controlled failure examples.&amp;nbsp;Denied and&amp;nbsp;warning states are useful because they prove the workflow can say no.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read-only default: start with inspection and diagnostics before allowing changes.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Confirmation gate: require explicit approval for medium-risk, write-like, or destructive actions.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Scope control: keep user, tenant, and schema filters close to the database query.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Failure trace: store denied calls and warnings as evidence instead of hiding them.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
&lt;strong&gt;Why put application memory records in Oracle AI Database, not just outputs &amp;nbsp;&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Once the first MCP tool calls work, the next challenge is continuity. This is where long-term memory for AI agents becomes different from&amp;nbsp;short-lived chat context.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;If memory lives only in chat context, the system is fragile. If memory is scattered across files without structure, retrieval and auditing become expensive over time.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;A database-backed memory model is usually cleaner and more scalable. The companion notebook builds this memory layer from&amp;nbsp;scratch&amp;nbsp;so the mechanics are visible, then shows how Oracle AI Agent Memory sits on top of it once the substrate is working.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory Categories That Matter in Practice:&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;You do not need to start by memorizing table names or column names.&amp;nbsp;The useful idea is simpler:&amp;nbsp;different kinds&amp;nbsp;of memory serve different jobs.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conversation memory: Keeps the important&amp;nbsp;user&amp;nbsp;and assistant&amp;nbsp;turns&amp;nbsp;that future sessions may need.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Operational memory: Keeps tool calls, outcomes, warnings, and failures so a team can debug what happened.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Semantic memory: Adds embeddings so the system can find relevant context even when the user asks in different words.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, hybrid retrieval for agent memory usually combines exact operational terms, such as &lt;code&gt;sql -mcp&lt;/code&gt; or &lt;code&gt;codex_mcp&lt;/code&gt;, with semantic search over memory records.&lt;/p&gt;

&lt;p&gt;The notebook shows&amp;nbsp;the lower-level mechanics&amp;nbsp;first&amp;nbsp;so the storage and retrieval path is visible.&amp;nbsp;This is also a context engineering problem: the application&amp;nbsp;has to&amp;nbsp;decide which memories, tool traces, and retrieval results should be assembled before Codex or another assistant answers.&amp;nbsp;Oracle AI Agent Memory then gives application code a cleaner&amp;nbsp;package&amp;nbsp;API over that same database-backed idea.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Where Oracle AI Agent Memory Fits&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Oracle AI Agent Memory sits between your application code and Oracle AI Database. The package manages conversation threads, durable memory records, scoped retrieval, and context assembly. Oracle AI Database&amp;nbsp;remains&amp;nbsp;the storage and enforcement layer underneath; the package does not replace&amp;nbsp;that,&amp;nbsp;it makes working with it faster and more consistent.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The notebook includes an abbreviated package-backed memory pattern. It initializes&amp;nbsp;&lt;code&gt;OracleAgentMemory&lt;/code&gt;&amp;nbsp;with a database connection pool and a custom local deterministic embedder.&amp;nbsp;&lt;code&gt;LocalCodexEmbedder&lt;/code&gt;&amp;nbsp;is notebook code, not a built-in Oracle AI Agent Memory embedder. &lt;/p&gt;

&lt;p&gt;The local embedder is intentionally billing-free, which makes the notebook runnable for people who do not want to attach paid model usage to a tutorial.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Abbreviated setup pattern: define&amp;nbsp;&lt;code&gt;CONFIG&lt;/code&gt;, import and initialize&amp;nbsp;&lt;code&gt;oracledb&lt;/code&gt;, implement&amp;nbsp;&lt;code&gt;LocalCodexEmbedder&lt;/code&gt;, and set stable values for&amp;nbsp;&lt;code&gt;AGENT_MEMORY_USER_ID&lt;/code&gt;&amp;nbsp;and&amp;nbsp;&lt;code&gt;AGENT_MEMORY_AGENT_ID&lt;/code&gt;&amp;nbsp;before running this example.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

```python 
from oracleagentmemory.core.oracleagentmemory import OracleAgentMemory 
from oracleagentmemory.apis.searchscope import SearchScope 
 
db_pool = oracledb.SessionPool( 
    user=CONFIG["ORACLE_USER"], 
    password=CONFIG["ORACLE_PASSWORD"], 
    dsn=CONFIG["ORACLE_DSN"], 
    min=1, 
    max=4, 
    increment=1, 
) 
 
agent_memory = OracleAgentMemory( 
    connection=db_pool, 
    embedder=LocalCodexEmbedder(dimensions=32), 
    llm=None, 
    extract_memories=False, 
    schema_policy="create_if_necessary", 
) 
```

 &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Use &lt;code&gt;oracleagentmemory&lt;/code&gt; from your application layer when you need package-managed users, agents, memories, threads, scoped retrieval, and context assembly.&amp;nbsp;Keep systems of record separate from memory records: memory helps provide context, but application logic and authoritative data sources should still decide what is true, allowed, and final.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Implementation&amp;nbsp;note:&amp;nbsp;Use a schema whose default tablespace supports the JSON objects created by Agent Memory. If a local SYSTEM schema sits on a tablespace that rejects JSON object creation, the better production answer is a dedicated application schema. The notebook can still explain the issue clearly instead of hiding it.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Store and Search: What a Realistic Memory Looks Like&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;A realistic Codex memory is not&amp;nbsp;generic&amp;nbsp;trivia about a user. For this workflow, memory should capture how a developer actually works: the connection name they used, the&amp;nbsp;SQLcl&amp;nbsp;path that succeeded, the MCP config location, the failed privilege boundary, the retrieval query that helped, and the final fix that should be reused later.&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

```python 
thread = agent_memory.create_thread(
    user_id=AGENT_MEMORY_USER_ID,
    agent_id=AGENT_MEMORY_AGENT_ID,
)
 
thread.add_memory( 
    "Developer validated Codex CLI with SQLcl MCP alias codex_mcp " 
    "against local Oracle AI Database service FREEPDB1." 
) 
 
results = agent_memory.search(
    query="Codex SQLcl MCP alias validation and Agent Memory setup", 
    scope=SearchScope(user_id=AGENT_MEMORY_USER_ID), 
) 
```

 &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That kind of memory pays off because it is operational. It can help Codex answer the next question with context from the&amp;nbsp;previous&amp;nbsp;debugging session, but it is still scoped and retrievable through a database-backed API.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Where&amp;nbsp;LangChain&amp;nbsp;Adds Value&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;LangChain&amp;nbsp;should not be treated as the source of truth. Codex CLI does not call&amp;nbsp;LangChain&amp;nbsp;directly in this architecture.&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;LangChain&amp;nbsp;runs in your application layer, where it can format context, wrap retrieval behavior, expose a familiar retriever shape, and plug Oracle-backed memory into chains or agents that already expect&amp;nbsp;LangChain&amp;nbsp;interfaces. In this notebook,&amp;nbsp;LangChain is used as a compatibility layer: the notebook’s custom Oracle-backed&amp;nbsp;&lt;code&gt;hybrid_search()&lt;/code&gt;&amp;nbsp;path performs hybrid retrieval, and its results are wrapped as LangChain&amp;nbsp;&lt;code&gt;Document&lt;/code&gt;&amp;nbsp;objects so they can flow into applications that already expect&amp;nbsp;LangChain&amp;nbsp;interfaces.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Oracle AI Database for persistence, retrieval, tenancy, validation evidence, and tool traces.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Use Oracle AI Agent Memory for package-backed memory threads, durable memories, and scoped search.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Use&amp;nbsp;LangChain&amp;nbsp;for retriever wrappers and orchestration compatibility when the application already expects that shape.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Use Codex CLI for developer interaction, planning, and guided execution.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The notebook's&amp;nbsp;LangChain&amp;nbsp;section is valuable because it comes late. By the time it appears, the database tables, package memory, retrieval scores, and validation snapshot already exist.&amp;nbsp;LangChain&amp;nbsp;becomes a wrapper around evidence, not a substitute for evidence.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Do not add&amp;nbsp;LangChain&amp;nbsp;just to make the workflow sound more agentic. If the application only needs direct SQL, package-backed Agent Memory search, or a simple evidence table, the extra abstraction can make debugging harder.&amp;nbsp;LangChain&amp;nbsp;is a good fit when the consuming application already expects retrievers, documents, chains, or&amp;nbsp;tool&amp;nbsp;orchestration. It is not the permission boundary, the memory store, or the audit layer.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Engineering Guidance&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;The difference between demo success and production success is disciplined operations. Most failures at this stage come from integration gaps, not model behavior. A local notebook can prove the wiring. A production workflow needs smaller database roles, managed secrets, clear MCP approval policy, repeatable environment setup, and&amp;nbsp;monitoring around&amp;nbsp;memory writes and tool calls.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Access and privilege model&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a dedicated application schema where possible, then tighten grants with least-privilege roles and quotas.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Keep saved&amp;nbsp;SQLcl&amp;nbsp;aliases separate by role and environment instead of sharing one broad connection.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Start read-only wherever possible and&amp;nbsp;gate write&amp;nbsp;operations with explicit confirmation workflows.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Use schema allowlists and separate accounts for development,&amp;nbsp;test, and production.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Observability model&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log tool name, thread ID, timestamp, status, and sanitized inputs and outputs.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Classify failures into runtime, discovery, connection, permission, query, and retrieval categories.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Keep a troubleshooting playbook in the&amp;nbsp;repo&amp;nbsp;so setup issues do not become tribal knowledge.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Check whether&amp;nbsp;retrieval&amp;nbsp;quality changes as more data and memory records are added.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reliability model&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer deterministic SQL patterns with bounded result sets.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Use retrieval-first context assembly for memory-heavy tasks.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Avoid giant context stuffing as a substitute for memory design.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Review and prune tool surfaces periodically.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Move from the local deterministic embedder to a supported embedding model after cost, latency, and privacy review.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
&lt;strong&gt;Typical Failure Modes&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Most teams hit a predictable set of issues.&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Runtime failure:&amp;nbsp;sql&amp;nbsp;-mcp&amp;nbsp;does not start&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Check the absolute&amp;nbsp;SQLcl&amp;nbsp;path, confirm Java is available, and run&amp;nbsp;sql&amp;nbsp;-mcp&amp;nbsp;outside Codex first. Resolve runtime issues before checking assistant behavior.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discovery failure: Codex does not see tools&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Check the Codex MCP configuration, confirm the configured command points to the&amp;nbsp;SQLcl&amp;nbsp;executable, and restart or reload Codex CLI after edits.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connection failure: tools are&amp;nbsp;present&amp;nbsp;but queries fail&amp;nbsp;immediately&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Check the saved&amp;nbsp;SQLcl&amp;nbsp;connection alias, confirm the profile lives under the expected&amp;nbsp;SQLcl&amp;nbsp;connection store, and verify password persistence for the MCP workflow. Then test the same connection outside Codex.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission failure: queries execute selectively and fail on specific objects&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Check the database role first. A selective failure can be the right outcome when&amp;nbsp;least&amp;nbsp;privilege is working. Add grants intentionally and keep read-write access separate from the&amp;nbsp;initial&amp;nbsp;validation path.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieval quality failure: answers are fluent but weakly grounded&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Inspect the retrieved records before blaming the model. Check chunk size, metadata filters, embedding choice, top-k settings, and whether the query is asking for exact history, semantic similarity, or operational&amp;nbsp;logs.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Why the Hybrid Model Is Usually the Best Long-Term Design&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;No single layer handles both execution and memory well. Trying to force everything into Codex context gets messy fast: you either lose control over execution, or you stuff too much state into prompts just to keep things working. On the other&amp;nbsp;side, if you only build backend memory systems, you lose the speed and usability that makes an assistant useful during development.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The hybrid approach works because it does not try to solve everything in one place. Execution stays controlled through MCP. Memory stays durable and&amp;nbsp;queryable&amp;nbsp;in Oracle AI Database. Oracle AI Agent Memory gives application code a cleaner memory API.&amp;nbsp;LangChain&amp;nbsp;adds orchestration only after evidence exists.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;In real&amp;nbsp;teams&amp;nbsp;this usually evolves over time. Start with Codex CLI,&amp;nbsp;SQLcl&amp;nbsp;MCP, read-only access, and strong logging. Once people rely on the workflow, the gaps become visible: lost context, weak traceability, repeated setup work, or retrieval that is hard to explain. That is when database-backed memory and structured retrieval become worth adding.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Conclusion&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;A Codex and&amp;nbsp;SQLcl&amp;nbsp;MCP workflow&amp;nbsp;becomes&amp;nbsp;useful when it is treated as an engineering pattern, not just a setup trick. Codex keeps the developer moving. MCP makes tool access&amp;nbsp;explicit.&amp;nbsp;SQLcl&amp;nbsp;connects those tools to&amp;nbsp;reviewed&amp;nbsp;Oracle profiles. Oracle AI Database stores durable memory records, traces, and retrieval evidence. Oracle AI Agent Memory gives applications a practical memory API.&amp;nbsp;LangChain&amp;nbsp;helps when retrieval needs to become&amp;nbsp;reusable&amp;nbsp;orchestration.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The result is a workflow a team can inspect. You can see what Codex asked for, which tool path ran, what the database allowed, which memory records were retrieved, and how the&amp;nbsp;final answer&amp;nbsp;was assembled.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;That is the shift that matters: from assistant access that is implicit and hard to audit, to explicit boundaries, durable memory, and evidence a developer can&amp;nbsp;actually debug.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Frequently Asked Questions&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is MCP in this context?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;MCP is the protocol boundary that lets Codex call explicit tools exposed by a server instead of accessing systems implicitly.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does MCP protect, and what does it not protect?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MCP makes the tool interface explicit and reviewable: Codex can call only the tools exposed by the server. It does not replace database security. The saved SQLcl connection profile, database user, grants, roles, network controls, and database policies determine what those tools can actually access or change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why use&amp;nbsp;SQLcl&amp;nbsp;for Oracle MCP?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;SQLcl&amp;nbsp;already understands Oracle workflows and can run as the Oracle&amp;nbsp;SQLcl&amp;nbsp;MCP server with &lt;code&gt;sql&amp;nbsp;–mcp&lt;/code&gt;, making the Oracle integration practical and direct.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this Codex CLI or Codex app?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The workflow should be described as Codex CLI/TUI oriented because the MCP configuration is written as a local Codex config&amp;nbsp;preview&amp;nbsp;and the&amp;nbsp;SQLcl&amp;nbsp;MCP server is launched as a command.&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why include Oracle AI Database if MCP already works?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;MCP handles the execution boundary. Oracle AI Database handles durable memory, retrieval, vector search, concurrency, observability, and governance.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need an OpenAI API key?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;An external model-provider API key is only needed if you change the notebook to use a provider-backed embedding or LLM service. The default notebook path uses a local deterministic embedder&amp;nbsp;so people can run the Agent Memory package section without adding billing details.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why use&amp;nbsp;SQLcl&amp;nbsp;MCP instead of connecting directly from Codex?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;MCP gives the workflow a defined tool boundary. Direct connections can be useful in notebooks, but Codex should call tools through a contract that can be logged, scoped, and reviewed.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why include&amp;nbsp;LangChain&amp;nbsp;if Oracle already stores memory?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Because many teams already have&amp;nbsp;LangChain-shaped retrievers and chains. The notebook shows how to expose Oracle-backed retrieval through that shape while keeping memory and enforcement in the database.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this RAG vs agent memory?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Not exactly. RAG retrieves external knowledge for a response, while agent memory preserves useful context, decisions, tool traces, and workflow state across sessions. In practice, production systems often use both.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the&amp;nbsp;minimum&amp;nbsp;viable&amp;nbsp;setup?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;SQLcl&amp;nbsp;MCP configured in Codex CLI, one safe saved Oracle connection, and a read-only validation flow.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should production start with read-write permissions?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Usually&amp;nbsp;no. Start read-only, log everything important, and add write scopes gradually with explicit approvals.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the best rollout strategy?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Pilot in development with read-only access and strong logging, then expand capabilities in controlled phases as the team learns which memory and tool paths are&amp;nbsp;actually useful.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Companion Troubleshooting Appendix&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Minimum&amp;nbsp;viable&amp;nbsp;setup:&lt;/strong&gt;&amp;nbsp;SQLcl&amp;nbsp;MCP configured in Codex CLI, one approved Oracle connection, read-only validation, and database-side activity logging. &amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First checks:&lt;/strong&gt;&amp;nbsp;confirm&amp;nbsp;&lt;em&gt;sql&amp;nbsp;-mcp&lt;/em&gt;&amp;nbsp;starts, Codex sees the tools after&amp;nbsp;restart, and the saved&amp;nbsp;SQLcl&amp;nbsp;connection alias resolves. &amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment model:&lt;/strong&gt;&amp;nbsp;use separate credentials and policies for dev, test, and prod, with stricter controls as capability expands. &amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Logging model:&lt;/strong&gt;&amp;nbsp;capture tool name, timestamp, thread ID, status, sanitized input/output summaries, and relevant&amp;nbsp;SQLcl&amp;nbsp;MCP log records. &amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieval quality:&lt;/strong&gt;&amp;nbsp;tune chunk size, enrich metadata, review embedding choice, and evaluate retrieval against representative queries. &amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common anti-pattern:&lt;/strong&gt;&amp;nbsp;expanding tool surfaces before ownership, logging standards, and runbooks are in place. &amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rollout path:&lt;/strong&gt;&amp;nbsp;pilot in dev with read-only access and strong logging, then expand capabilities in controlled phases. &amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema&lt;/strong&gt;: Agent Memory package tables need a schema and tablespace that can create the package's JSON-backed objects.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model calls&lt;/strong&gt;: an API key can be present but still fail if the model provider account has no quota.&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;
&lt;strong&gt;Resources&amp;nbsp;&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/" rel="noreferrer noopener"&gt;Oracle AI Agent Memory documentation&lt;/a&gt;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/" rel="noopener noreferrer"&gt;Oracle Database 26ai documentation&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://pypi.org/project/oracleagentmemory/" rel="noreferrer noopener"&gt;oracleagentmemory on PyPI&lt;/a&gt;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.2/sqcug/sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;SQLcl MCP Server&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/codex_mcp_oracle_ai_database_memory_langchain.ipynb" rel="noopener noreferrer"&gt;End-to-end support notebook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



</description>
      <category>codex</category>
      <category>oracle</category>
      <category>database</category>
      <category>ai</category>
    </item>
    <item>
      <title>Agent memory is a database problem: Oracle research makes the case</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Tue, 11 Aug 2026 15:16:38 +0000</pubDate>
      <link>https://dev.to/oracledevs/agent-memory-is-a-database-problem-oracle-research-makes-the-case-1p0l</link>
      <guid>https://dev.to/oracledevs/agent-memory-is-a-database-problem-oracle-research-makes-the-case-1p0l</guid>
      <description>&lt;p&gt;&lt;em&gt;A technical report on arXiv sets out a database-native memory substrate for long-horizon agents: 93.8% on LongMemEval, roughly 10.7× fewer input tokens than flat conversation history, and governance treated as a design constraint, not an afterthought.&lt;/em&gt;&lt;/p&gt;





&lt;h2&gt;Key Takeaways&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Agent memory is fundamentally a data-management problem. Long-horizon agents need persistent, scoped, retrievable, and governable memory, not just larger context windows or more conversation history.&lt;/li&gt;



&lt;li&gt;Oracle Agent Memory manages working memory, long-term factual memory, and procedural memory directly on Oracle AI Database, with a lifecycle spanning extraction, consolidation, retrieval, summarisation, and revision.&lt;/li&gt;



&lt;li&gt;In the report, Oracle Agent Memory achieved 93.8% on LongMemEval and kept estimated input context to about 1,300 tokens per request at turn 80, compared with roughly 13,900 for flat conversation history.&lt;/li&gt;



&lt;li&gt;Keeping agent memory in the database keeps it close to the enterprise data it reasons over, while bringing transactions, relational, JSON and vector capabilities, scoped retrieval, and database-enforced governance into the same substrate.&lt;/li&gt;
&lt;/ul&gt;





&lt;p&gt;Prompt and context engineering emerged for a reason: to elicit specific behaviours from large language models whilst maintaining a high signal-to-noise ratio in the context window, through systematic curation of the data injected into it. But as models grow more capable at reasoning, extensive context-window management is becoming less necessary.&lt;/p&gt;

&lt;p&gt;The real job to be done sits elsewhere: adapting agents to new information, and making them dependable on long-horizon tasks. That job is the subject of a new technical report from Oracle's AI teams, &lt;a href="https://arxiv.org/pdf/2607.13157" rel="noopener noreferrer"&gt;&lt;em&gt;Oracle Agent Memory as an Enterprise Memory Substrate for Long-Horizon AI Agents&lt;/em&gt;&lt;/a&gt;, published this month on arXiv.&lt;/p&gt;





&lt;p&gt;&lt;strong&gt;Agent memory has emerged as the solution: the capability that lets agentic systems learn, adapt, and personalise from continuous interaction between the agent, its users, and its environment.&lt;/strong&gt; The report grounds this in recognisable enterprise use cases: support agents recalling incident history across weeks, coding agents reusing prior design decisions, analytics agents that know what "average spending" means in the business.&lt;/p&gt;

&lt;p&gt;The core problem investigated is not recall; it is maintaining heterogeneous memory under explicit scope, retention, and retrieval constraints; this is effective memory management. Yet although many memory-management solutions have emerged, few have met the demanding requirements of enterprise AI workloads. Most are variations on "just add a vector store": fragments assembled around the model (reflection buffers, framework add-ons, standalone memory services) that each work in a demo, and that together complicate governance, security, data locality, and reliability in an enterprise setting.&lt;/p&gt;

&lt;p&gt;The gap is enterprise-grade memory: a substrate where governance and security are entry requirements rather than roadmap items, and where scale, reliability, and data locality come built in rather than bolted on. The report is written squarely at that gap. Its starting observation is simple: the list of things agent memory demands (persistence, indexing, scoped retrieval, transactions, governance) is a list of database requirements.&lt;/p&gt;





&lt;p&gt;&lt;strong&gt;Oracle Agent Memory is a database-native memory substrate that treats agent memory as a managed lifecycle, spanning ingestion, extraction, consolidation, retrieval, summarisation, and revision, built directly on Oracle AI Database.&lt;/strong&gt; The report organises that memory into three operational categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Working memory&lt;/strong&gt;: the active state of a task, materialised as thread summaries and context cards compact enough for prompt injection.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Long-term factual memory&lt;/strong&gt;: durable facts, preferences, and profile attributes that persist across sessions under user and agent scope.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Procedural memory&lt;/strong&gt;: lessons, strategies, and guidelines drawn from prior outcomes; what the agent has learned to do, not just what it knows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An active memory core does the opinionated work: synchronising threads, extracting durable facts, refreshing summaries and context cards, orchestrating search. Beneath it sits a passive memory-store contract (insert, search, retrieve, enforce scope) that stays reusable even for third-party memory libraries wanting Oracle AI Database as their backend.&lt;/p&gt;

&lt;p&gt;The report measures the substrate across long-horizon memory benchmarks, a flat-history baseline, and estimated token behaviour:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;93.8% on LongMemEval&lt;/strong&gt; (469 of 500), including 100% on single-session assistant recall and 96.2% on temporal reasoning. Multi-session reasoning is the honest low point at 88%, and the report says so.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Roughly 10.7× fewer estimated input tokens&lt;/strong&gt; than a flat-history baseline by turn 80 of a scripted conversation: about 1,300 tokens per request against roughly 13,900.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;48 wins, 13 losses, 19 ties&lt;/strong&gt; in pairwise judging against that same flat-history baseline, a 3.7× win ratio among non-tied turns, even though the baseline saw the entire transcript. Bounded, relevant context beat complete, unfocused context.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;0.680 on BEAM at the 1M-token scale&lt;/strong&gt; under event-presence scoring, ahead of previously reported external results, with the stricter order-sensitive score (0.630) and an early 10M stress test (0.510) published alongside it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For enterprise leaders&lt;/strong&gt;, the argument is about where institutional memory should live. Agent memory holds private conversations, inferred preferences, learned procedures, and retrieved enterprise facts. Store it in a side service with its own authorisation model and you replicate identity, role, and policy outside your primary data platform. Place it in the database and memory stays close to the enterprise data it reasons over, inheriting the controls you already trust: with database-enforced access control through Oracle Deep Data Security, a denied record is absent from retrieval because the acting identity was never allowed to see it, not because an application filter caught it afterwards. &lt;strong&gt;Memory stops being another shadow data store to govern.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For developers, the report reads like an engineering document, because it is one. Threads are the integration primitive, and the workflow has four recurring stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Initialise the memory client with a database connection, an embedder, and an optional LLM.&lt;/li&gt;



&lt;li&gt;Create or reopen a thread, scoped to a user and an agent.&lt;/li&gt;



&lt;li&gt;Add messages so the system persists history and runs extraction and summarisation.&lt;/li&gt;



&lt;li&gt;Retrieve context via scoped search, context cards, or thread summaries before the next turn.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Defaults are conservative: bounded reads, bounded search, explicit scope with exact-match controls, no hidden schema side effects. The &lt;a href="https://www.oracle.com/database/ai-agent-memory/" rel="noopener noreferrer"&gt;&lt;code&gt;oracleagentmemory&lt;/code&gt;&lt;/a&gt; Python client is model-agnostic at the embedder and LLM layer, slots into harnesses such as LangGraph and WayFlow, and supports three integration patterns (middleware-centric, tool-centric, or hybrid). The appendices cover setup, thread lifecycle, and search semantics in runnable detail.&lt;/p&gt;

&lt;p&gt;The report also stakes out an evaluation position both audiences should note: downstream accuracy alone is not enough. A correct answer can hide noisy retrieval; a wrong one can hide a memory layer that did its job. It measures memory as a system instead, separating evidence retrieval, evidence use, task outcome, and operational efficiency, and applies that standard to itself, publishing configuration caveats, threshold sweeps, and failure modes in plain sight.&lt;/p&gt;

&lt;p&gt;Every team building agents eventually rediscovers the same requirements list, and then rebuilds it around a vector store. The position behind Oracle Agent Memory is that the list already has an answer. Give agents a substrate that speaks transactions, vectors, JSON, and policy natively, and the effort moves to where it actually matters: managing the lifecycle of what an agent remembers, not plumbing where it stores it. &lt;strong&gt;The storage question is settled. Remembering is the work.&lt;/strong&gt;

The full report is available on arXiv: https://arxiv.org/pdf/2607.13157

&lt;/p&gt;

&lt;h2&gt;FAQs&lt;/h2&gt;

&lt;h3&gt;What is agent memory?&lt;/h3&gt;

&lt;p&gt;Agent memory is the persistent state that lets an AI agent retain and retrieve useful information across interactions. That can include active task context, facts and preferences learned over time, and procedures or strategies derived from previous outcomes. Unlike the context window, that information does not have to disappear when the current conversation ends.&lt;/p&gt;

&lt;h3&gt;How is agent memory different from RAG or a vector database?&lt;/h3&gt;

&lt;p&gt;Vector retrieval can be part of agent memory, but retrieval alone is not memory management. An agent also needs to decide what to retain, how that information is scoped, when it should be updated or summarised, how long it should persist, and who is allowed to retrieve it. The Oracle report treats those as lifecycle and data-management problems rather than just search problems.&lt;/p&gt;

&lt;h3&gt;Why use a database for AI agent memory?&lt;/h3&gt;

&lt;p&gt;Agent memory needs many of the capabilities databases already provide: persistence, indexing, structured and semantic retrieval, transactions, access control, and governance. A database-native approach also allows relational data, JSON, vectors, and memory state to live on the same managed substrate rather than being distributed across separate memory services.&lt;/p&gt;

&lt;h3&gt;How does Oracle Agent Memory integrate with agent applications?&lt;/h3&gt;

&lt;p&gt;Developers create or reopen a thread scoped to a user and agent, add messages, and retrieve relevant context before subsequent turns. The &lt;code&gt;oracleagentmemory&lt;/code&gt; Python client is model-agnostic at the embedding and LLM layers and supports middleware-centric, tool-centric, and hybrid integration patterns, including with frameworks such as LangGraph and WayFlow.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>agentskills</category>
      <category>database</category>
      <category>oracle</category>
    </item>
    <item>
      <title>Custom Memory Extraction for AI Agents: Turning Conversations into Useful Facts</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Tue, 11 Aug 2026 15:11:32 +0000</pubDate>
      <link>https://dev.to/oracledevs/custom-memory-extraction-for-ai-agents-turning-conversations-into-useful-facts-3752</link>
      <guid>https://dev.to/oracledevs/custom-memory-extraction-for-ai-agents-turning-conversations-into-useful-facts-3752</guid>
      <description>&lt;p&gt;Learn how &lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.6/guide/" rel="noreferrer noopener"&gt;Oracle AI Agent Memory&lt;/a&gt; uses custom extraction instructions, thread-level overrides, and tool-result metadata to turn support conversations into durable, scoped memory.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Companion notebook: &lt;/strong&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/agent_memory/custom_memory_extraction_agent_memory.ipynb" rel="noreferrer noopener"&gt;custom_memory_extraction_agent_memory.ipynb&lt;/a&gt;&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Key Takeaways&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Oracle AI Agent Memory can transform raw user-agent messages into durable memories stored in Oracle AI Database.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Default extraction provides a general memory baseline; custom extraction instructions make memory formation reflect a domain-specific policy.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;A customer-support agent should preserve durable facts such as order IDs, return requests, delivery issues, escalation commitments, tool-confirmed statuses, and stable communication preferences.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Good memory is selective: the extractor should ignore greetings, speculation, credentials, payment details, one-time codes, and temporary conversational wording.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Client-level custom instructions define the default extraction behavior, while thread-level overrides allow narrower policies for special workflows such as escalations.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Tool-result metadata can be inherited into memories, making later search more scoped, auditable, and aligned with enterprise retrieval boundaries.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;The notebook verifies database connectivity and table-creation permissions before running memory operations.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;





&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2F1-Custom-Extraction-Flow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2F1-Custom-Extraction-Flow.png" alt="Flowchart showing support messages and tool results entering an Oracle AI Agent Memory extraction workflow governed by a custom policy that preserves durable support facts and ignores transient details. The workflow produces durable memories such as facts, preferences, and commitments plus inherited tenant, source, and tag metadata. Scoped retrieval then selects relevant memory for prompt-ready support context." width="800" height="699"&gt;&lt;/a&gt; Custom extraction turns raw conversation into governed memory&amp;nbsp;&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Why Agents Need Selective Memory&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.6/guide/" rel="noreferrer noopener"&gt;Oracle AI Agent Memory&lt;/a&gt; provides a database-backed memory layer for AI agents, so applications can store messages, durable memories, metadata, summaries, and prompt-ready context in &lt;a href="https://www.oracle.com/database/ai-database/" rel="noreferrer noopener"&gt;Oracle AI Database&lt;/a&gt;.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;AI agents are most useful when they can carry context forward. A support agent may need to remember that a customer reported a damaged item, prefers SMS updates, or was promised an escalation by Friday. Without persistent memory, the agent has to rediscover these facts from chat history or ask the user again.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;But remembering everything is not the same as remembering well. Real conversations contain greetings, clarifications, temporary codes, apologies, repeated facts, speculation, and sometimes sensitive information. If every detail becomes durable memory, future retrieval becomes noisier and harder to govern.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Custom memory extraction addresses this by turning extraction into an application policy. Instead of only asking the model to summarize a conversation, developers can define what their workflow considers useful memory and what should be ignored.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;The Customer-Support Use Case&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;The companion notebook uses a customer-support workflow because it has a realistic mix of natural language, exact identifiers, tool results, and policy boundaries. This is the kind of workflow where a generic transcript summary is not enough.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The support conversation includes durable facts such as order identifiers, return intent, delivery problems, replacement commitments, and customer preferences. It also includes details that should not become long-term memory, such as temporary wording, credentials, payment details, and speculation.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preserve exact identifiers such as order IDs, return IDs, and case IDs.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Preserve confirmed support facts such as delivery issues, product defects, return requests, and replacement commitments.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Preserve stable preferences that should influence future interactions.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Preserve tool-confirmed facts when a tool result is the source of truth.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Ignore small talk, unconfirmed guesses, secrets, payment details, and one-time codes.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2F2-Support-Policy-Boundary.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2F2-Support-Policy-Boundary.png" alt="Flowchart showing a support conversation processed by custom extraction instructions. The policy preserves order IDs, return requests, delivery issues, commitments, and stable preferences while ignoring small talk, speculation, credentials, payment secrets, and one-time codes. Preserved information becomes concise, exact, reusable durable memory." width="800" height="732"&gt;&lt;/a&gt;Customer-support extraction policy&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;From Conversation Text to Durable Memory&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Oracle AI Agent Memory stores the conversation and uses an extraction workflow to form higher-level memories. In a support case, those memories should be concise, durable, and useful for later retrieval. A memory like "Customer prefers morning delivery windows for replacement shipments" is more reusable than several raw turns where the user mentioned their preference indirectly.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The notebook first establishes a baseline extraction path. This baseline is useful because it shows the general extraction behavior before any domain-specific policy is added. The custom path then uses the same kind of support conversation but adds explicit extraction instructions.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SUPPORT_EXTRACTION_INSTRUCTIONS = """
Extract only durable customer-support memory that can help future support interactions.

Preserve confirmed order IDs, return requests, delivery issues, escalation reasons,
stable customer preferences, and tool-derived support facts.

Ignore greetings, small talk, speculation, credentials, payment secrets,
one-time verification codes, and temporary conversational wording.
""".strip()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These instructions guide the LLM-assisted extraction step. They help shape what should become durable memory, but they should not be treated as deterministic filtering, guaranteed redaction, or a replacement for application-level security controls.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;This instruction block is intentionally written like a product policy. It does not ask the extractor to remember more; it asks the extractor to remember better. The policy defines durable support memory, instructs the extractor to preserve exact identifiers, and excludes information that should not influence future agent behavior.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Baseline Extraction vs. Custom Extraction&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;The notebook shows the comparison directly. A baseline client uses the package's general extraction behavior. A custom client uses the same support scenario with custom extraction instructions. This makes the effect of the policy visible rather than theoretical.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from oracleagentmemory.core import MemoryExtractionConfig, OracleAgentMemory

base_memory = OracleAgentMemory(
    store=base_store,
    llm=memory_llm,
    memory_extraction_config=MemoryExtractionConfig(
        memory_extraction_frequency=1,
        enable_context_summary=False,
    ),
)

custom_memory = OracleAgentMemory(
    store=custom_store,
    llm=memory_llm,
    memory_extraction_config=MemoryExtractionConfig(
        memory_extraction_frequency=1,
        enable_context_summary=False,
        memory_extraction_custom_instructions=SUPPORT_EXTRACTION_INSTRUCTIONS,
    ),
)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The important design point is that the application owns the memory policy. The model helps extract memories, but the application defines what kinds of facts should survive, which exact identifiers matter, and what kinds of text should be excluded.&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Because this policy is evaluated by an LLM-assisted extraction step, it should be treated as guidance rather than deterministic filtering, guaranteed redaction, or a replacement for application-level security controls.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;For the reader, the expected outcome is straightforward: baseline extraction demonstrates general memory formation; custom extraction demonstrates memory formation shaped by support workflow priorities.&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;What to Look for in the Output&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;The most important signal is not that the custom path creates more memory records. The stronger signal is that the memory records are better shaped for the workflow. The custom extraction path should preserve durable support facts while avoiding temporary or sensitive details that should not guide future conversations.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The order identifier remains available as an exact support reference.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;The return or replacement context is preserved as durable case state.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;The customer's stable delivery or communication preference is retained.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Temporary details, such as a one-time lobby or access code, are not treated as reusable memory.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Tool-confirmed status can be preserved with metadata that describes the source and workflow tag.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This before-and-after comparison is what makes custom extraction practical: it shows how memory can become more useful, searchable, and governable without turning the full transcript into long-term state.&amp;nbsp;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Signal in the notebook&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Baseline extraction&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Custom extraction&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exact support identifiers&amp;nbsp;&lt;/td&gt;
&lt;td&gt;May preserve the identifier if it is generally salient.&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Explicitly preserves order, return, and case identifiers as durable support references.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Return or replacement state&amp;nbsp;&lt;/td&gt;
&lt;td&gt;May summarize the conversation at a general level.&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Keeps confirmed return, replacement, delivery, and escalation facts as reusable case state.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customer preference&amp;nbsp;&lt;/td&gt;
&lt;td&gt;May capture the preference if it appears important in the conversation.&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Preserves stable communication and delivery preferences that should influence future interactions.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temporary detail&amp;nbsp;&lt;/td&gt;
&lt;td&gt;May not have a domain-specific reason to exclude it.&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Treats one-time codes, temporary instructions, and conversational noise as non-durable.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool-confirmed fact&amp;nbsp;&lt;/td&gt;
&lt;td&gt;May treat tool output like another message.&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Preserves tool-derived support facts and selected source metadata for scoped retrieval.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;





&lt;h2&gt;
&lt;strong&gt;Client-Level Defaults and Thread-Level Overrides&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Not every conversation in an application needs the same extraction policy. Most support threads may follow a broad support-memory policy, while an escalation thread may need a narrower policy focused only on shipment commitments and customer-facing follow-up deadlines.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Oracle AI Agent Memory supports this pattern by allowing custom extraction instructions at the client level and overrides at the thread level. The client-level policy becomes the default. A thread-level override can refine the policy for one conversation.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;escalation_thread = custom_memory.create_thread( 
    thread_id=f"escalation_support_{RUN_ID}", 
    user_id=USER_ID, 
    agent_id=AGENT_ID, 
    memory_extraction_config=MemoryExtractionConfig( 
        memory_extraction_frequency=1, 
        enable_context_summary=False, 
        memory_extraction_custom_instructions=ESCALATION_EXTRACTION_INSTRUCTIONS, 
    ), 
) &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The notebook also shows lifecycle control for thread-level instructions. A workflow can update the thread policy when the support state changes, or clear the override to return to the client-level default.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;updated_escalation_thread = custom_memory.update_thread( 
    escalation_thread.thread_id, 
    memory_extraction_config=MemoryExtractionConfig( 
        memory_extraction_custom_instructions=( 
            "Extract only shipment escalation commitments and customer-facing follow-up deadlines." 
        ) 
    ), 
) 
 
cleared_escalation_thread = custom_memory.update_thread( 
    escalation_thread.thread_id, 
    memory_extraction_config=MemoryExtractionConfig( 
        memory_extraction_custom_instructions=None 
    ), 
) &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FGroup-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FGroup-2.png" alt="Comparison of baseline and custom memory extraction for the same support conversation containing an order ID, replacement request, delivery preference, and temporary code. Baseline extraction may capture salient facts without support-specific rules. Custom extraction keeps the ID and preference while ignoring the temporary code, producing better-shaped memory for future support turns." width="800" height="735"&gt;&lt;/a&gt;Client-level policy and thread-level override&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Common Patterns for Custom Extraction Instructions&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Custom extraction instructions are flexible by design. Developers do not need a different memory API for every workflow. Instead, they can express the workflow's memory policy through instructions passed into the extraction configuration.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The useful pattern is to write instructions as a boundary: what to preserve, what to ignore, and which source or metadata signals should influence the extracted memory. The following patterns are not separate package objects; they are practical ways to structure `memory_extraction_custom_instructions` for common agent workflows.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Pattern&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Use when&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Instruction focus&lt;/strong&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Support memory policy&amp;nbsp;&lt;/td&gt;
&lt;td&gt;A support agent needs durable case context across future interactions.&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Preserve order IDs, return IDs, delivery issues, replacement commitments, escalation reasons, and stable preferences. Ignore greetings, credentials, payment details, one-time codes, and temporary wording.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Escalation-only policy&amp;nbsp;&lt;/td&gt;
&lt;td&gt;One thread needs a narrower policy than the default client-level behavior.&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Preserve escalation reason, owner or team, customer-facing follow-up deadline, and confirmed commitments. Ignore general troubleshooting and repeated background context.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool-result policy&amp;nbsp;&lt;/td&gt;
&lt;td&gt;The agent receives tool outputs that should be treated as authoritative workflow state.&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Preserve tool-confirmed facts, keep exact identifiers, inherit selected metadata such as tenant, source, and tags, and distinguish tool-confirmed facts from user-reported claims.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;





&lt;h2&gt;
&lt;strong&gt;Tool-Aware Extraction with Metadata&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Tool-aware extraction is especially useful in enterprise workflows because many agents do not rely only on user messages. They call tools, retrieve operational state, and produce tool results that may be more authoritative than the natural-language conversation.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;In the support notebook, a tool result can carry metadata such as tenant, source, and tags. The extraction policy can tell the memory layer to preserve tool-derived support facts, and selected metadata can be inherited into the extracted memory.&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;tool_thread = custom_memory.create_thread( 
    thread_id=f"tool_support_{RUN_ID}", 
    user_id=USER_ID, 
    agent_id=AGENT_ID, 
    memory_extraction_config=MemoryExtractionConfig( 
        memory_extraction_frequency=1, 
        enable_context_summary=False, 
        memory_extraction_custom_instructions=TOOL_AWARE_EXTRACTION_INSTRUCTIONS, 
        memory_extraction_inherit_message_metadata=["tenant", "source", "tags"], 
    ), 
) &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This pattern lets the application preserve not only the memory text, but also where the remembered fact came from. Later, scoped retrieval can use both natural-language query text and metadata filters.&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;tool_filtered_results = await tool_thread.search_async( 
    "replacement shipment delayed backordered hinge escalate", 
    max_results=5, 
    exact_thread_match=True, 
    record_types=["memory", "fact", "preference", "guideline"], 
    metadata_filter={ 
        "tenant": "acme", 
        "source": "support-copilot", 
        "tags": ["support", "tool:order_status", "replacement"], 
    }, 
) &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FGroup-12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F08%2FGroup-12.png" alt="Diagram showing two controls on support memory. Tool results inherit selected tenant, source, and tag metadata before becoming tool-confirmed memories used in scoped search. Separately, a client-level default memory policy can be overridden for a thread, such as an escalation-only policy, then updated or cleared as workflow state changes." width="800" height="710"&gt;&lt;/a&gt;Tool-result metadata and scoped retrieval&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Why Metadata Matters for Memory Governance&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Memory retrieval should be relevant, but it also needs boundaries. In enterprise systems, an application may need to search only within a tenant, case, workflow, source system, or review state. Similarity ranking alone is not an authorization or governance model. Metadata filters complement, but do not replace, application authentication and authorization.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Metadata gives the application a way to constrain which memories are eligible before ranking. In this article's use case, metadata can distinguish support-copilot facts from raw conversation messages, tag memories that came from an order-status tool, or keep tenant-specific facts scoped to the right customer context.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;This is also why custom extraction and metadata inheritance belong together. Custom extraction shapes the content of the memory. Metadata inheritance shapes how that memory can be retrieved, filtered, reviewed, and governed later.&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Running the Companion Notebook&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;The companion notebook starts with a practical database setup path. The setup cells connect to an Oracle AI Database schema, verify a simple query, and verify table-creation permissions before the memory store is initialized.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;For a lightweight trial environment, developers can use &lt;a href="https://freesql.com/" rel="noreferrer noopener"&gt;FreeSQL&lt;/a&gt; to get an Oracle AI Database schema before moving to a managed development or production database environment.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;That table-permission check matters because Oracle AI Agent Memory manages database-backed storage objects. If a developer can connect but cannot create tables, the memory workflow will fail later during schema setup. Surfacing that issue early makes the notebook more practical.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The notebook also sets the Developer Hub program identifier before creating the database connection pool. This follows the Developer Hub convention for technical assets that connect to Oracle AI Database and makes the notebook identifiable in database telemetry.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;For environments with a database-resident embedding model, the same pattern can use Oracle AI Database-backed embeddings and Oracle AI Vector Search. For the custom extraction article, the main focus remains memory formation: how raw conversations become durable, domain-specific memories with the right policy and metadata.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;For database-level retrieval background, see the &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noreferrer noopener"&gt;Oracle AI Vector Search documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the Companion Notebook Demonstrates&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect to Oracle AI Database and verify table permissions.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Set the Developer Hub program identifier before opening the database connection.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Configure Oracle AI Agent Memory for a support workflow.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Add a support conversation containing exact identifiers, support commitments, preferences, and details that should not become memory.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Run baseline extraction as a general-purpose reference point.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Apply support-specific custom extraction instructions.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Compare extracted memories before and after customization.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Override extraction instructions for one escalation thread.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Update and clear thread-level extraction instructions.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Extract tool-derived support facts and inherit selected metadata.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Search with metadata filters to retrieve scoped memory results.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Clean up memory records and database objects after the run.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
&lt;strong&gt;Security and Deployment Notes&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Custom extraction instructions are not a substitute for application security controls. Oracle AI Agent Memory persists data in the database schema selected by the application owner, while the surrounding application remains responsible for authenticating users, enforcing access control, and passing the correct user, agent, and thread scope into memory operations.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Applications should avoid sending secrets or unnecessary sensitive information into memory ingestion flows unless that behavior is explicitly intended and reviewed. Extraction instructions can guide what the model should preserve or ignore, but they should be treated as policy guidance rather than a hard redaction guarantee.&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep credentials, payment details, and one-time verification codes out of memory ingestion when possible.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Use exact user, agent, thread, and tenant scope for retrieval.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Use metadata filters for workflow boundaries such as source system, review status, or tool tag.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Review custom extraction instructions with domain owners, not only with developers.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Use cleanup steps in notebooks and development schemas to avoid leaving test data behind.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
&lt;strong&gt;Conclusion&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Custom memory extraction makes agent memory more useful by making it selective. Instead of treating every conversation as durable knowledge, developers can define what their application should preserve, which identifiers matter, what metadata should follow extracted memories, and when a specific thread needs a narrower policy.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;For customer-support agents, this means future interactions can draw on the right facts: order IDs, delivery issues, return requests, escalation commitments, stable preferences, and tool-confirmed statuses. The agent gets memory that is easier to retrieve, easier to scope, and better aligned with the workflow it serves.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;A practical way to start is with a narrow extraction policy, inspect the extracted memories, and then expand the policy and metadata taxonomy with domain owners.&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Frequently Asked Questions&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is custom memory extraction?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Custom memory extraction lets developers guide what Oracle AI Agent Memory should preserve as durable memory and what it should ignore when processing conversation messages.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why should an agent not remember everything?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Full transcripts contain temporary wording, repeated details, speculation, and sometimes sensitive data. Durable memory should preserve reusable facts, preferences, commitments, and workflow state.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the difference between baseline and custom extraction?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Baseline extraction shows general-purpose memory formation. Custom extraction adds domain-specific instructions so the memory output reflects the application's workflow policy.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is a thread-level extraction override?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;A thread-level override is a custom extraction policy applied to one thread. It can narrow or change the default client-level policy for a specific workflow such as an escalation.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does tool metadata help?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Tool metadata records where a memory came from and how it should be scoped. It can help retrieve only memories from a specific tenant, source system, or tool result.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the notebook require FreeSQL?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;No. FreeSQL is a lightweight trial setup path. The same memory APIs can run against another Oracle AI Database environment with the required credentials and permissions.&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Resources&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/agent_memory/custom_memory_extraction_agent_memory.ipynb" rel="noreferrer noopener"&gt;Companion notebook&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/oracleagentmemory/26.6.0/" rel="noreferrer noopener"&gt;Oracle AI Agent Memory package&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.6/guide/" rel="noreferrer noopener"&gt;Oracle AI Agent Memory documentation&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://freesql.com/" rel="noreferrer noopener"&gt;FreeSQL&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noreferrer noopener"&gt;Oracle AI Vector Search documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>database</category>
      <category>ai</category>
      <category>agents</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>Hybrid Search for Oracle AI Agent Memory: Combining Semantic Recall with Exact Match</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Tue, 11 Aug 2026 14:59:04 +0000</pubDate>
      <link>https://dev.to/oracledevs/hybrid-search-for-oracle-ai-agent-memory-combining-semantic-recall-with-exact-match-29aj</link>
      <guid>https://dev.to/oracledevs/hybrid-search-for-oracle-ai-agent-memory-combining-semantic-recall-with-exact-match-29aj</guid>
      <description>&lt;p&gt;&lt;strong&gt;Use&amp;nbsp;Oracle AI Agent Memory hybrid search when persistent AI agent memory needs both semantic similarity and exact text precision.&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Companion Notebook:&lt;/strong&gt; &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/agent_memory/hybrid_search_agent_memory.ipynb" rel="noopener noreferrer"&gt;Hybrid Search for Oracle AI Agent Memory: Combining Semantic Recall with Exact Match&lt;/a&gt;&lt;/p&gt;





&lt;h2&gt;Key Takeaways&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noreferrer noopener"&gt;Vector search&lt;/a&gt; is strong for semantic recall, but agent memory often contains exact strings that should remain first-class retrieval signals.&lt;/li&gt;



&lt;li&gt;Hybrid search combines semantic retrieval and exact text matching so agents can recall both the named item and the surrounding context.&lt;/li&gt;



&lt;li&gt;Scoped retrieval and hybrid search solve different parts of the problem: scope controls which memories are eligible to be searched, while hybrid search controls how those eligible memories are ranked.&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/agmea/get-started.html" rel="noreferrer noopener"&gt;Oracle AI Agent Memory&lt;/a&gt; uses `SearchStrategy.HYBRID` with `OracleDBEmbedder`, while `SearchIndexSyncMode` controls managed search-index refresh behavior.&lt;/li&gt;



&lt;li&gt;The companion notebook validates hybrid retrieval across five exact, semantic, and mixed-query scenarios using deliberately similar memories and confirms the expected memory at rank one in every scenario.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide shows how to build&amp;nbsp;hybrid retrieval for agent memory&amp;nbsp;with Oracle AI Agent Memory, combining semantic recall with exact text matching.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;The retrieval problem hybrid search solves&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;A useful agent memory system&amp;nbsp;has to&amp;nbsp;do two things at once. It must remember by meaning when the user paraphrases, and it must stay exact when the user refers to a specific business object, system event, or operational handle. Many retrieval misses happen in the gap between those two needs: the query is semantically close to several memories, but only one memory&amp;nbsp;contains&amp;nbsp;the exact identifier that should anchor the answer.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;Hybrid search closes that gap. Instead of treating semantic recall and exact text matching as separate retrieval systems, it gives the memory layer a combined path for vector and keyword search. The agent can retrieve the surrounding context and preserve the literal token that makes the memory trustworthy.&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;That is the core value of agent memory hybrid search: long-term agent memory can stay flexible without losing the exact text that enterprise workflows depend on.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;This article starts with a simple retrieval problem: a user may ask about a business event in natural language, then later refer to the same event by an invoice number, issue ID, error code, customer alias, or filename. A useful memory system should handle both query styles.&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Setting up Oracle&amp;nbsp;AI&amp;nbsp;Agent Memory&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/agmea/" rel="noreferrer noopener"&gt;Oracle AI Agent Memory&lt;/a&gt;&amp;nbsp;provides a durable memory layer for AI agents. It stores scoped memories, retrieves relevant context for later turns, and helps applications separate what the agent is allowed to&amp;nbsp;remember&amp;nbsp;from how memories are ranked.&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Hybrid search expands that retrieval model so an agent can recall by semantic meaning and by exact text in the same workflow, while memory governance&amp;nbsp;remains&amp;nbsp;tied to scope and application policy.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;The setup has four main components:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;`OracleAgentMemory`&amp;nbsp;is the durable memory layer. It stores memories, applies scope, and exposes search.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;`OracleDBEmbedder`&amp;nbsp;connects retrieval to Oracle-backed embedding&amp;nbsp;behavior&amp;nbsp;used by the memory store.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;`SearchStrategy.HYBRID`&amp;nbsp;tells the memory layer to combine semantic and exact-text retrieval signals.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;`SearchIndexSyncMode`&amp;nbsp;controls when the managed search index is refreshed after memories are written.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start by installing the&amp;nbsp;oracleagentmemory&amp;nbsp;package in the notebook or application environment. The hybrid-search API is configured in application code; the database schema and index setup are handled through the Oracle-backed memory store.&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;This makes Oracle AI Database agent memory a practical pattern for applications that need durable memories, scoped retrieval, and database-managed search.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

```bash 
pip install oracleagentmemory==26.6.0 
```

 &amp;nbsp;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The core imports for a hybrid-search configuration look like this:&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

```python 
from oracleagentmemory.apis.searchscope import SearchScope 
from oracleagentmemory.core import ( 
    MemoryExtractionConfig, 
    OracleAgentMemory, 
    SchemaPolicy, 
    SearchIndexSyncMode, 
    SearchStrategy, 
) 
from oracleagentmemory.core.embedders import OracleDBEmbedder 
```

 &lt;/code&gt;&lt;/pre&gt;





&lt;h2&gt;
&lt;strong&gt;Why vector memory is not always enough&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Agent memory is not only a semantic archive of&amp;nbsp;previous&amp;nbsp;conversations. In enterprise systems, memory records often include short strings that carry operational meaning.&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;A single identifier can point to the correct incident, invoice, customer, model, file, run, or database error. If retrieval treats that identifier like ordinary prose, the agent can return a memory that is semantically related but operationally wrong.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Vector search helps when users paraphrase. It is especially useful when a user asks about a topic without repeating the exact words stored in memory.&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;But vector similarity is&amp;nbsp;not the same as&amp;nbsp;exactness. A short token can be diluted by the rest of the sentence, especially when the surrounding context is broad or when several memories are about similar workflows.&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Where exact text matters&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Exact text matters whenever a memory must point to a specific object rather than a general topic. Common examples include:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Issue IDs and ticket references used in engineering and support workflows.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Invoice, order, contract, and purchase-order numbers used in business workflows.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Database, application, and integration error codes used in troubleshooting.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Customer aliases, account IDs, and tenant names used for scoped recall.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Product SKUs, model numbers, filenames, branch names, and run IDs used by operational agents.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These values are small, but they often carry more authority than nearby prose. A robust agent memory layer should preserve that literal signal while still supporting semantic lookup and exact-match retrieval.&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Types of&amp;nbsp;search&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Vector search&lt;/td&gt;
&lt;td&gt;Keyword search&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Hybrid search&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finds by meaning and paraphrase&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Finds by exact text (literal identifiers)&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Combines semantic&amp;nbsp;and exact-text signals in one ranked retrieval path&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example query: “What blocked the renewal?”&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Example query: “INV-48291”&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Example queries: “INV-48291” and “What blocked the renewal?”&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
Table&amp;nbsp;1:&amp;nbsp;Conceptual&amp;nbsp;search-mode comparison&amp;nbsp;


&lt;p&gt;The differences become clearer when you compare what each mode&amp;nbsp;optimizes&amp;nbsp;for.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The following comparison explains the retrieval modes conceptually. The companion notebook executes the hybrid configuration and evaluates it across exact, semantic, and mixed queries; it is not a benchmark of three separately executed strategies.&amp;nbsp;&lt;/p&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mode&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Best at&lt;/td&gt;
&lt;td&gt;Weak spot&lt;/td&gt;
&lt;td&gt;Agent memory fit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector search&lt;/td&gt;
&lt;td&gt;Paraphrase, semantic similarity, concept recall&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Short identifiers may be&amp;nbsp;low-signal&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Good for natural-language recall&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keyword search&lt;/td&gt;
&lt;td&gt;Literal identifiers, error codes, aliases, filenames&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Brittle when wording changes&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Good when exact strings matter&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid search&lt;/td&gt;
&lt;td&gt;Natural language plus exact handles&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Requires database embedding and managed index setup&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Strong fit when both semantic and exact-text retrieval matter&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
Table&amp;nbsp;2: Search mode comparison&amp;nbsp;


&lt;p&gt;Use hybrid search when:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users ask in natural language but refer to exact business objects.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Memories include identifiers, aliases, filenames, error codes, or transaction IDs.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Several memories are semantically similar, but one exact token should decide the result.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;The agent needs scoped retrieval across user, agent, tenant, or thread boundaries.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
&lt;strong&gt;How Oracle AI Agent Memory supports hybrid retrieval for agent memory&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;Oracle AI Agent Memory exposes hybrid retrieval as a memory-store configuration. The application supplies an Oracle-backed embedder, selects&amp;nbsp;`SearchStrategy.HYBRID`, and chooses an index synchronization mode. The result is one memory layer that can retrieve by meaning and exact text over the same stored memories, while still respecting user, agent, and thread scope.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;`OracleDBEmbedder`&amp;nbsp;is important because hybrid search relies on the database-managed embedding and index path.&amp;nbsp;`SearchStrategy.HYBRID`&amp;nbsp;selects the hybrid retrieval backend.&amp;nbsp;`SearchIndexSyncMode`&amp;nbsp;defines when new or updated memory content becomes searchable through the managed index. Together, these settings make retrieval&amp;nbsp;behavior&amp;nbsp;explicit in application code rather than hiding it behind an ad hoc query pipeline.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The configuration below shows the&amp;nbsp;minimum&amp;nbsp;pattern: choose an Oracle-backed embedder, set&amp;nbsp;`SearchStrategy.HYBRID`, and choose how the search index should refresh:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

```python 
db_embedder = OracleDBEmbedder( 
    connection=connection, 
    model=CONFIG["ORACLE_DB_EMBEDDING_MODEL"], 
    embedding_dimension=CONFIG["ORACLE_DB_EMBEDDING_DIMENSION"], 
) 
memory = OracleAgentMemory( 
    connection=connection, 
    embedder=db_embedder, 
    memory_extraction_config=MemoryExtractionConfig( 
        extract_memories=False 
    ), 
    schema_policy=SchemaPolicy.CREATE_IF_NECESSARY, 
    search_strategy=SearchStrategy.HYBRID, 
    search_index_sync=SearchIndexSyncMode.ON_COMMIT, 
    memory_store_id="hybrid_blog", 
) 
```

 &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Memory extraction is disabled because the example inserts controlled durable memories directly and focuses specifically on retrieval&amp;nbsp;behavior.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;In a typical workflow, the agent writes durable memories with user, agent, or thread scope. Later, search requests use&amp;nbsp;`SearchScope`&amp;nbsp;to restrict eligible records before ranking happens. That separation matters: scope decides what the agent is allowed to remember, while hybrid search decides how eligible memories are ranked.&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Hybrid retrieval flow&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;In an agent workflow, hybrid search is most useful when it sits between memory scoping and context assembly:&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F07%2FContainer-538x1024.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F07%2FContainer-538x1024.png" alt="Flowchart titled &amp;quot;Scoped Hybrid Retrieval.&amp;quot; A user query passes through a SearchScope filter to select eligible durable memories. Those memories are searched using hybrid retrieval, combining semantic and exact matching. The ranked results are then assembled into the prompt context for the language model. Each stage is connected by downward arrows, showing a linear retrieval pipeline." width="538" height="1024"&gt;&lt;/a&gt;Figure&amp;nbsp;1.&amp;nbsp;Scoped hybrid retrieval flow&lt;p&gt;&lt;/p&gt;

&lt;p&gt;`SearchScope`&amp;nbsp;and hybrid search are complementary. Scope&amp;nbsp;determines&amp;nbsp;which records are eligible for retrieval; hybrid search ranks only those eligible records. In the notebook, each evaluation query uses the same generated finance-user and support-agent scope, and retrieval is restricted to durable memory records by passing&amp;nbsp;`record_types=["memory"]`.&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;This flow is the reason hybrid search fits agent memory well. The application first narrows the memory universe with scope. Hybrid retrieval then combines semantic and exact-text signals only over eligible records. The final output is not just a matching row; it is a ranked context package the agent can use in the next step.&amp;nbsp;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

```python 
user_id = "finance_user_123" 
agent_id = "support_finance_agent" 
  
scope = SearchScope(user_id=user_id, agent_id=agent_id) 
  
memory.add_memory( 
    content=( 
        "Northstar Renewals has a renewal blocker: invoice INV-48291 "
        "failed reconciliation after ORA-27102 during month-end processing." 
    ), 
    user_id=user_id, 
    agent_id=agent_id, 
) 
 
async def search_memory(query, scope, max_results=5): 
    return await memory.search_async( 
        query=query, 
        scope=scope, 
        max_results=max_results, 
        record_types=["memory"], 
    ) 
 
exact_results = await search_memory("INV-48291", scope) 
 
semantic_results = await search_memory( 
    "What blocked the Northstar renewal?", 
    scope, 
) 
 ```

 &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Both calls use the same `SearchScope`, hybrid strategy, and managed hybrid index. Only the query style changes.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;For example, a user might ask “INV-48291” or “What blocked the renewal?” over the same scoped memories.&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;A vector-only search may understand the renewal-blocker question but underweight the exact invoice or error code. A keyword-only search may find INV-48291 or ORA-27102 but miss a paraphrased question such as “What blocked the renewal?”&amp;nbsp;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Hybrid search gives both signals a chance to influence ranking, so the agent can retrieve the Northstar Renewals memory with the exact invoice ID, the ORA-27102 error code, and the natural-language explanation.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Testing hybrid retrieval with deliberately similar memories&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;The companion notebook does not test hybrid retrieval against a single obvious memory. It creates a deliberately similar set of&amp;nbsp;memories,&amp;nbsp;so the retrieval path&amp;nbsp;has to&amp;nbsp;choose between records that share overlapping business, invoice, customer, renewal, and error-code vocabulary.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The notebook stores five durable memories:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A target renewal-blocker memory for Northstar Renewals,&amp;nbsp;containing&amp;nbsp;invoice INV-48291, Oracle error ORA-27102, and the failed reconciliation that blocked the renewal.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;A&amp;nbsp;neighboring&amp;nbsp;invoice memory for Northstar Renewals,&amp;nbsp;containing&amp;nbsp;invoice INV-48290, which is marked as paid and does not require follow-up.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;A different customer renewal-delay memory for Milan Office Supplies, where the delay is caused by an approval workflow rather than reconciliation.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;A general ORA-27102 troubleshooting memory that explains the database error without tying it to the Northstar invoice.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;A Northstar customer-alias memory that maps Northstar Renewals to its enterprise account name.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup makes the demonstration more realistic. The expected result must outrank memories that are partially similar, such as another Northstar invoice, another renewal delay, or another ORA-27102-related record. That is the&amp;nbsp;behavior&amp;nbsp;an enterprise agent memory system needs: not just finding something&amp;nbsp;related but&amp;nbsp;retrieving the memory that preserves the right operational detail.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Evaluating exact, semantic, and mixed queries&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;The notebook then evaluates the same hybrid-search configuration across five query scenarios. Each query uses the same&amp;nbsp;`SearchScope`, the same&amp;nbsp;`SearchStrategy.HYBRID`&amp;nbsp;configuration, and the same managed hybrid index. Only the query wording changes.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The five query categories are:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exact invoice identifier: a query for INV-48291.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Exact Oracle error code: a query for ORA-27102.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Semantic renewal-blocker question: a natural-language question about what blocked the Northstar renewal.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Semantic customer question: a natural-language question about the Milan customer renewal delay.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Mixed customer and invoice question: a question that combines customer context with invoice reconciliation.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The validation checks whether the expected memory appears as the top-ranked result.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Query type&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Expected memory&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Rank&lt;/td&gt;
&lt;td&gt;Validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exact invoice ID&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Renewal blocker&amp;nbsp;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PASS&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exact error code&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Error reference&amp;nbsp;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PASS&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Semantic renewal question&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Renewal blocker&amp;nbsp;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PASS&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Semantic customer question&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Milan renewal&amp;nbsp;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PASS&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mixed customer and invoice question&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Renewal blocker&amp;nbsp;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PASS&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
Table&amp;nbsp;3: Evaluation results table&amp;nbsp;


&lt;p&gt;This is a controlled functional evaluation, not a statistical benchmark comparing vector, keyword, and hybrid search. The purpose is to show that one hybrid-search configuration can retrieve the expected durable memory across exact, semantic, and mixed query styles.&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Reading the notebook results&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;The results show that exact identifiers remained useful retrieval signals. The invoice query returned the renewal-blocker memory, while the ORA-27102 query returned the general error-reference memory. This matters because both the target renewal memory and the general troubleshooting memory&amp;nbsp;contain&amp;nbsp;the same error code, but they serve different user intents.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The semantic queries also returned the intended memories without requiring the user to repeat the exact stored wording. For example, the renewal-blocker question retrieved the Northstar renewal memory, while the Milan customer question retrieved the separate Milan renewal-delay memory.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The mixed query is the most representative enterprise case. It combines business context with an implied operational object: the user asks which invoice failed reconciliation for Northstar. The expected result is the memory&amp;nbsp;containing&amp;nbsp;INV-48291, ORA-27102, and the failed reconciliation context.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The notebook also displays distance values for the ranked results. In this result set, smaller distance values&amp;nbsp;indicate&amp;nbsp;stronger matches. However, distance should be treated as a retrieval signal for inspection, not as a universal accuracy score. The more important validation is whether the expected memory was returned at rank one for each query.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;How hybrid search improves agent memory retrieval&lt;/h2&gt;

&lt;p&gt;Hybrid search can improve retrieval behavior because it matches how enterprise memory is actually written. A memory record rarely contains only natural-language explanation or only an identifier. It usually contains both: a named handle plus the context that explains why the handle matters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Higher precision for identifier-driven questions because exact strings can influence ranking.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Better recall for paraphrased questions because semantic search still finds related memory records.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;More useful context packages because the agent can retrieve both the handle and the surrounding explanation.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Lower risk of plausible but wrong recall when several memories are semantically similar.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Developers can inspect the ranked memories, metadata, and distances to verify whether the expected operational context was retrieved.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially useful for support agents, finance assistants, operations copilots, developer agents, and workflow agents that must remember prior decisions, tool outputs, system errors, or customer-specific context.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;The practical benefit is not only better search quality; it is better agent&amp;nbsp;behavior.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Better retrieval can improve grounding, but the model still needs normal validation and application controls.&amp;nbsp;Hybrid search gives the retrieval layer a stronger chance of selecting the memory that a human operator would have recognized&amp;nbsp;immediately.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Production notes: schema and index setup&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;For production, treat hybrid search as part of the memory-store design, not as a last-minute query&amp;nbsp;option. The schema should be owned by a dedicated application user with the right privileges, tablespace quota, and deployment controls. Memory scope should also match the application boundary, such as user, agent, tenant, or thread.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;`SchemaPolicy.CREATE_IF_NECESSARY` is useful during first-time setup or when upgrading a schema so it can support hybrid search. For a large existing memory store, the first hybrid index build may take time because Oracle needs to prepare the managed search structures over stored memory content. Treat that step as a planned migration rather than a normal application startup task.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;After the schema and index are ready, production applications may prefer `SchemaPolicy.REQUIRE_EXISTING`. That lets startup&amp;nbsp;validate&amp;nbsp;the expected schema instead of creating or&amp;nbsp;modifying&amp;nbsp;database objects.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;`SearchIndexSyncMode` is a freshness and operations&amp;nbsp;tradeoff:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;`ON_COMMIT` is best for notebooks, demos, and interactive applications because newly committed memories become searchable&amp;nbsp;immediately.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;&amp;nbsp;`MANUAL` is useful for bulk loads, backfills, and migrations where teams want to ingest many records first and refresh the index on a controlled schedule.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;`AUTO` delegates background maintenance to Oracle-managed&amp;nbsp;behavior, which can fit production workloads where some freshness lag is acceptable. `AUTO` is supported for `SearchStrategy.HYBRID`; keyword-only search does not support `AUTO`.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The database-resident embedding model is also part of the production contract. The same model and embedding dimension should be used by `OracleDBEmbedder` and the managed hybrid index so query embeddings and indexed memory vectors&amp;nbsp;remain&amp;nbsp;compatible.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Before publishing an agent that depends on durable memory, test the retrieval path with representative records. Include exact-identifier queries, semantic queries, mixed queries, scoped retrieval checks, and index freshness checks after writes. If the application has many similar records, include distractor memories in the test set so the expected result&amp;nbsp;has to&amp;nbsp;outrank nearby alternatives.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Teams can also pair hybrid retrieval with custom memory extraction when they need more predictable stored facts. Extraction controls what gets remembered; scope controls which records are eligible; hybrid search controls how eligible memories are ranked.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Conclusion&lt;/strong&gt;&amp;nbsp;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;AI agent memory needs more than semantic similarity. Vector search helps agents remember by meaning, but enterprise workflows often depend on exact strings such as invoice numbers, error codes, customer aliases, file names, and transaction IDs. Keyword search helps with those strings, but it can be brittle when users ask in natural language.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Hybrid search gives the memory layer a practical middle ground. It preserves literal identifiers while still supporting semantic recall, so an agent can retrieve both the named object and the context that explains why it matters.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;In the companion notebook, one `SearchStrategy.HYBRID` configuration retrieves the expected durable memory across exact, semantic, and mixed query styles. The deliberately similar memory set makes the demo more realistic: the target memory&amp;nbsp;has to&amp;nbsp;outrank records that overlap on customer, invoice, renewal, or error-code vocabulary.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;For Oracle AI Agent Memory, `SearchStrategy.HYBRID`, `OracleDBEmbedder`, `SearchScope`, and `SearchIndexSyncMode`&amp;nbsp;provide the core application controls for this pattern. Together, they help developers build memory systems that are scoped, durable, searchable by meaning, and precise when exact text matters.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Run the companion notebook to configure Oracle AI Agent Memory hybrid search, load the deliberately similar memory set, and inspect the ranked results for exact, semantic, and mixed queries.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Frequently Asked Questions&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is Oracle AI Agent Memory?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Oracle AI Agent Memory is a durable memory layer for AI agents. It lets applications store memories, retrieve relevant context later, and use scope controls such as user, agent, and thread scope so the agent searches only eligible memories before ranking results.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;strong&gt;Why use hybrid search for agent memory&lt;/strong&gt;?&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Hybrid search is useful for agent memory because many memories&amp;nbsp;contain&amp;nbsp;both meaning and exact text. Vector search helps with paraphrased questions, while keyword search helps with identifiers such as issue IDs, SKUs, invoice numbers, aliases, and error codes. Hybrid search combines both signals so the agent can retrieve the right memory and the surrounding context.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;strong&gt;Is vector search still useful for agent memory?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Yes. Vector search&amp;nbsp;remains&amp;nbsp;essential for paraphrase, concept recall, and long natural-language memories. Hybrid search adds exact-text strength instead of replacing vector search.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I build hybrid retrieval for agent memory?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Configure&amp;nbsp;OracleAgentMemory with `OracleDBEmbedder`, set&amp;nbsp;`SearchStrategy.HYBRID`, choose&amp;nbsp;`SearchIndexSyncMode`, store scoped memories, then test exact, semantic, and mixed queries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When should I use keyword-only search?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Use keyword-only search when retrieval is&amp;nbsp;almost entirely&amp;nbsp;literal text matching and embeddings are unnecessary. Many agent memory workloads need both exact strings and semantic context, which makes hybrid search a better default.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the difference between scoped retrieval and hybrid search?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Scoped retrieval controls which memories are eligible to be searched. Hybrid search controls how those eligible memories are ranked using both semantic and exact-text signals.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;strong&gt;Why does hybrid search require&amp;nbsp;`OracleDBEmbedder`?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The hybrid path uses Oracle-managed indexing and database-side embedding metadata.&amp;nbsp;`OracleDBEmbedder`&amp;nbsp;keeps the application embedder aligned with the database-backed retrieval path.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;strong&gt;Which&amp;nbsp;`SearchIndexSyncMode`&amp;nbsp;should I start with?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;For notebooks and interactive demos,&amp;nbsp;`ON_COMMIT`&amp;nbsp;is the simplest starting point. For bulk ingestion,&amp;nbsp;`MANUAL`&amp;nbsp;gives more control.&amp;nbsp;`AUTO`&amp;nbsp;is useful when Oracle-managed hybrid index maintenance fits the workload.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;strong&gt;What should I test before publishing an agent memory workflow?&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Test exact-identifier queries, semantic queries, scoped retrieval, index freshness after writes, and&amp;nbsp;behavior&amp;nbsp;when multiple similar memories exist. The strongest demos show both exact and natural-language retrieval over the same records.&amp;nbsp;&lt;/p&gt;





&lt;h2&gt;
&lt;strong&gt;Resources&amp;nbsp;&lt;/strong&gt;&amp;nbsp;&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/" rel="noreferrer noopener"&gt;Oracle AI Agent Memory documentation&lt;/a&gt;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/" rel="noopener noreferrer"&gt;Oracle Database 26ai documentation&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/agmea/store-search.html" rel="noreferrer noopener"&gt;Store and Search Memory&amp;nbsp;documentation&lt;/a&gt;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://pypi.org/project/oracleagentmemory/" rel="noreferrer noopener"&gt;oracleagentmemory on PyPI&lt;/a&gt;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/index.html" rel="noreferrer noopener"&gt;Oracle AI Vector Search User’s Guide&lt;/a&gt;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://blogs.oracle.com/developers/whats-new-in-oracle-ai-agent-memory-custom-extraction-hybrid-search-and-more-control" rel="noopener noreferrer"&gt;What’s New in Oracle AI Agent Memory: Custom Extraction, Hybrid Search, and More Control&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/agent_memory/hybrid_search_agent_memory.ipynb" rel="noopener noreferrer"&gt;End-to-end support notebook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



</description>
      <category>agentskills</category>
      <category>agents</category>
      <category>oracle</category>
      <category>database</category>
    </item>
    <item>
      <title>Building Scalable Multi-Language Applications with Oracle Globally Distributed Database Using OCI-Based Drivers</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Tue, 11 Aug 2026 14:30:21 +0000</pubDate>
      <link>https://dev.to/oracledevs/building-scalable-multi-language-applications-with-oracle-globally-distributed-database-using-2je7</link>
      <guid>https://dev.to/oracledevs/building-scalable-multi-language-applications-with-oracle-globally-distributed-database-using-2je7</guid>
      <description>&lt;p&gt;Modern enterprise applications are rarely built using a single programming language. A cloud-native application might use Python for AI and analytics, Node.js for REST APIs and C for latency-sensitive services. Despite the diversity of languages, the database-access goals are familiar: efficient connection pooling, intelligent routing, high availability, and predictable scalability.&lt;/p&gt;

&lt;p&gt;Oracle Globally Distributed Database (GDD) enables applications to scale horizontally by distributing data across shards while presenting a single logical database to the application. To make these capabilities available consistently across languages, Oracle provides OCI-based (Oracle Call Interface) drivers that expose the same distributed database features through language-native APIs.&lt;/p&gt;

&lt;p&gt;In this article, we'll explore how Python, Node.js and C applications use the same OCI foundation to build scalable, shard-aware applications.&lt;/p&gt;





&lt;h2&gt;Key Takeaways&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python and Node.js in Thick mode and C through OCI(Oracle Call Interface) give developers a consistent core workflow for Oracle Globally Distributed Database: create or reuse sessions, supply shard keys when needed, and release connections predictably. The APIs and feature coverage remain language-specific.&lt;/li&gt;



&lt;li&gt;Shard-aware connection routing is consistent across languages. Applications acquire connections using sharding keys (and optionally super sharding keys), allowing Oracle Globally Distributed Database to automatically route requests to the appropriate shard.&lt;/li&gt;



&lt;li&gt;Thoughtful pool sizing and high-availability configuration are essential in multi-shard systems: they support session reuse, help avoid uneven connection distribution, and can improve recovery behavior when the required services and client configuration are in place.&lt;/li&gt;
&lt;/ul&gt;





&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F07%2FOracle-GDD-OCI-Based-Drivers-Architecture-v4-1024x576.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F07%2FOracle-GDD-OCI-Based-Drivers-Architecture-v4-1024x576.png" alt="Flowchart showing database sharding request routing. Python Thick, Node.js Thick, and C/OCI clients feed into drivers and a connection pool. A sharding key and super sharding key determine routing, followed by direct routing or connection checkout to the data-owning shard. The request executes on that shard, then the connection is released." width="800" height="450"&gt;&lt;/a&gt;&lt;em&gt;Use Python, Node.js, and C for shard-aware connection pooling and resilient database access.&lt;/em&gt;&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;A Common Foundation&lt;/h2&gt;

&lt;p&gt;One of the strengths of Oracle's language drivers is that they share a common implementation stack. Although the programming APIs differ, they ultimately leverage the same Oracle Call Interface (OCI) capabilities for connection management, sharding, and high availability. In Thick mode, the Python and Node.js drivers use Oracle Client libraries and Oracle Call Interface (OCI). C applications call OCI directly. &lt;/p&gt;

&lt;p&gt;This means that developers can choose the programming language best suited to their workload while relying on consistent database behavior.&lt;/p&gt;

&lt;h4&gt;Why Thick Mode?&lt;/h4&gt;

&lt;p&gt;Thick mode is the Python and Node.js deployment mode that uses Oracle Client libraries and the Oracle Call Interface (OCI). It gives those applications access to the OCI-based connection behavior that C applications use directly. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High-performance session pooling enables session reuse, reducing connection-creation overhead under load and helping applications manage demand across shards.&lt;/li&gt;



&lt;li&gt;Sharding and super sharding keys let the client request a connection for the data-owning shard, without embedding physical shard locations in application logic.&lt;/li&gt;



&lt;li&gt;Oracle Net and the native-client or provider capabilities supply the connection behavior needed for enterprise deployments, including the routing information used to establish the right connection.&lt;/li&gt;



&lt;li&gt;High availability is configured separately from driver selection. FAN, Application Continuity or Transparent Application Continuity (TAC), and Transparent Application Failover (TAF) can improve failure response and continuity during planned or unplanned events, but their availability and defaults depend on the driver or provider type, client version, database service, and pool or connection configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Initializing Oracle Client enables Thick mode for Python and Node.js only. It does not, by itself, enable high-availability behavior. The same principle applies across C/OCI: configure and test the relevant services, client settings, and pool behavior for the driver in use.&lt;/em&gt;&lt;/p&gt;





&lt;h2&gt;Creating a Scalable Session Pool&lt;/h2&gt;

&lt;p&gt;Connection pooling is the first step toward scalable access. Instead of creating a new database connection for every request, applications reuse existing sessions, lowering latency and improving throughput. In a multi-shard system, pool configuration also matters because demand may not be evenly distributed across shards. Monitor pool utilization and shard-level workload patterns, then tune capacity for the traffic you actually observe.&lt;/p&gt;

&lt;p&gt;The pool sizes below are illustrative only, not recommended defaults. Size pools using expected concurrency, request duration, database capacity, shard distribution, and service-level objectives.&lt;/p&gt;

&lt;h5&gt;Python&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;import oracledb

oracledb.init_oracle_client()

pool = oracledb.create_pool(
    user=user,
    password=password,
    dsn=connect_string,
    min=5,
    max=50,
    increment=5,
    getmode=oracledb.POOL_GETMODE_WAIT
)&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;Node.js&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;const oracledb = require('oracledb');

oracledb.initOracleClient();

const pool = await oracledb.createPool({
    user,
    password,
    connectString,
    poolMin: 5,
    poolMax: 50,
    poolIncrement: 5
});&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;C &lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;OCIEnvCreate(...);

OCIHandleAlloc(...);

OCISessionPoolCreate(
    envhp,
    errhp,
    poolhp,
    &amp;amp;poolName,
    &amp;amp;poolNameLen,
    connectString,
    strlen(connectString),
    minSessions,
    maxSessions,
    increment,
    username,
    strlen(username),
    password,
    strlen(password),
    OCI_SPC_HOMOGENEOUS
);&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Acquiring Shard-Aware Connections&lt;/h3&gt;

&lt;p&gt;The key benefit of shard-aware access is not that the application learns where shards live. It is that the client can direct work to the shard that owns the requested data. Supplying the sharding key and, where the data model uses one, the super sharding key helps avoid unnecessary data movement and keeps physical topology out of business logic. The following snippets are abbreviated examples. They omit credentials, error handling, variable declarations, pool shutdown, and full OCI environment and handle management. Production code should add those concerns and should release every acquired connection&lt;/p&gt;

&lt;h5&gt;Python&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;# The context manager releases the connection to the pool.
with pool.acquire(
    supershardingkey=[region],
    shardingkey=[customer_id]
) as connection:
    with connection.cursor() as cursor:
        cursor.execute(sql)&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;Node.js&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;let connection;
try {
  connection = await pool.getConnection({
    superShardingKey: [region],
    shardingKey: [customerId]
  });
  await connection.execute(sql);
} finally {
  if (connection) await connection.close(); // Return it to the pool
}&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;C&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;/* Create an authInfo handle, allocate key descriptors, add values,
   attach both keys, then request the pooled session.
   Error checks are omitted here. */
OCIAuthInfo *authInfo = NULL;
OCIShardingKey *shardKey = NULL, *superShardKey = NULL;

OCIHandleAlloc(envhp, (dvoid **)&amp;amp;authInfo,
               OCI_HTYPE_AUTHINFO, 0, NULL);

OCIDescriptorAlloc(envhp, (dvoid **)&amp;amp;shardKey,
                   OCI_DTYPE_SHARDING_KEY, 0, NULL);
OCIDescriptorAlloc(envhp, (dvoid **)&amp;amp;superShardKey,
                   OCI_DTYPE_SHARDING_KEY, 0, NULL);

OCIShardingKeyColumnAdd(shardKey, errhp, &amp;amp;customerId,
                         sizeof(customerId), SQLT_INT, OCI_DEFAULT);
OCIShardingKeyColumnAdd(superShardKey, errhp, region,
                         strlen((char *)region), SQLT_CHR, OCI_DEFAULT);

OCIAttrSet(authInfo, OCI_HTYPE_AUTHINFO, shardKey, sizeof(shardKey),
           OCI_ATTR_SHARDING_KEY, errhp);
OCIAttrSet(authInfo, OCI_HTYPE_AUTHINFO, superShardKey, sizeof(superShardKey),
           OCI_ATTR_SUPER_SHARDING_KEY, errhp);

OCISessionGet(envhp, errhp, &amp;amp;svcHandle, authInfo,
              poolName, poolNameLen, NULL, 0, NULL, NULL, NULL,
              OCI_SESSGET_SPOOL);

/* Execute work on the routed shard, then release the session. */
OCISessionRelease(svcHandle, errhp, NULL, 0, OCI_DEFAULT);
OCIDescriptorFree(shardKey, OCI_DTYPE_SHARDING_KEY);
OCIDescriptorFree(superShardKey, OCI_DTYPE_SHARDING_KEY);
OCIHandleFree(authInfo, OCI_HTYPE_AUTHINFO);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Although the APIs differ, each performs the same sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Construct the sharding key.&lt;/li&gt;



&lt;li&gt;Request a pooled connection.&lt;/li&gt;



&lt;li&gt;Allow OCI to route the request to the correct shard.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;Comparing the Drivers&lt;/h3&gt;

&lt;p&gt;The table helps an architect choose the API that matches the application language while preserving the same operating model. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Capability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Python (Thick)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Node.js (Thick)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;C&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Client / driver&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Oracle Client&lt;/td&gt;
&lt;td&gt;Oracle Client&lt;/td&gt;
&lt;td&gt;Direct OCI calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Session pool&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;create_pool()&lt;/td&gt;
&lt;td&gt;createPool()&lt;/td&gt;
&lt;td&gt;OCISessionPoolCreate()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shard-aware checkout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;pool.acquire()&lt;/td&gt;
&lt;td&gt;pool.getConnection()&lt;/td&gt;
&lt;td&gt;OCISessionGet()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sharding / super sharding keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Language-native parameters&lt;/td&gt;
&lt;td&gt;Language-native parameters&lt;/td&gt;
&lt;td&gt;OCIShardingKey + OCIAttrSet()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Connection release&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Context manager or close()&lt;/td&gt;
&lt;td&gt;connection.close()&lt;/td&gt;
&lt;td&gt;OCISessionRelease()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The APIs are intentionally language-specific, while the core pooling and shard-aware routing workflow is consistent. Individual feature availability varies by driver mode, client version, database service, and configuration.&lt;/p&gt;





&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Oracle's OCI-based drivers provide a unified foundation for building scalable applications across multiple programming languages. Whether applications are written in Python, Node.js or C, developers have access to the same core capabilities—session pooling, shard-aware routing, high availability, and efficient native connectivity—through familiar language APIs.&lt;/p&gt;

&lt;p&gt;The result is a more consistent deployment and operating model across the application stack, with work directed to the data-owning shard and with less duplicated connection-management knowledge between language teams.&lt;/p&gt;





&lt;h2&gt;Frequently Asked Questions&lt;/h2&gt;

&lt;h4&gt;Why should I use Thick mode instead of Thin mode?&lt;/h4&gt;

&lt;p&gt;Thin mode is suitable for many Oracle Database applications, but Thick mode uses Oracle Client libraries and the Oracle Call Interface (OCI). For Oracle Globally Distributed Database, it gives Python and Node.js applications access to OCI-based shard-aware connections and advanced connection management. Features such as Fast Application Notification (FAN), Application Continuity, Transparent Application Continuity (TAC), and Transparent Application Failover (TAF) require support from the relevant driver mode and version, database services, and client or pool configuration. Initializing Oracle Client enables Thick mode for Python and Node.js; it does not by itself enable high-availability behavior.&lt;/p&gt;

&lt;h4&gt;Why do I need sharding keys?&lt;/h4&gt;

&lt;p&gt;Sharding keys allow Oracle Globally Distributed Database to determine which shard contains the requested data. By supplying sharding keys when acquiring connections, applications are automatically routed to the appropriate shard, minimizing cross-shard communication and improving performance.&lt;/p&gt;

&lt;h4&gt;Do I need to know which shard my data resides on?&lt;/h4&gt;

&lt;p&gt;No. Applications simply provide the appropriate sharding key. Oracle Globally Distributed Database handles the routing transparently, allowing developers to focus on application logic rather than shard location.&lt;/p&gt;

&lt;h4&gt;Do all drivers have the same programming model and capabilities?&lt;/h4&gt;

&lt;p&gt;They share a consistent core workflow: configure a client and pool, request a connection with routing information when needed, perform work, and release the connection. The APIs are intentionally language-native, and availability of individual features can vary by driver mode, client version, database service, and configuration.&lt;/p&gt;





&lt;h2&gt;References&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/lnoci/oci-interface-for-using-shards.html" rel="noopener noreferrer"&gt;Oracle Call Interface documentation&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html" rel="noopener noreferrer"&gt;Python Oracle DB&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://node-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#examples-to-connect-to-a-globally-distributed-database-based-on-the-sharding-key-type" rel="noopener noreferrer"&gt;Node.js Oracle DB&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>oracle</category>
      <category>multilanguage</category>
      <category>applications</category>
    </item>
  </channel>
</rss>
