Part 2 of 5

Wiring Oracle AI Database 26ai to Claude Code

MCP in Action for Oracle Professionals

Data engineering with AI, starting from the credential boundary. A walkthrough of the groundwork that has to be in place before an agent can reach an Oracle AI Database 26ai instance.

By Charles April 15, 2026 7 min read

In Part 1, I demonstrated how we can configure Claude Code (Claude) to talk to the database through SQLcl with a password stored in the wallet and a .mcp.json scoped to the project. For Part 2, let's dive deeper into MCP capabilities by building out a consultant utilization report built entirely inside Claude, with the SQLcl MCP server as the only thing communicating with the database. We will have Claude perform the ETL/ELT functions by filtering out data from a spreadsheet, changing consultant names and changing company names to ensure confidentiality. Then Claude will leverage the MCP server to create the table, load the data into the table, perform queries to calculate utilization and provide a comprehensive dashboard of the consultant's utilization.

If you want to follow along, grab the sample CSV here: fictious-time.csv. It is the same 1,084-row dataset used throughout this post.

Background Information

The dataset is a real timesheet export from our consulting practice: consultant, client, project, task, hours, billable flag. Claude's first task for this post wasn't running a query. It was preparing and cleansing the data.

Swapping Real Names for Fictitious Ones with Claude Code

I started with the time.csv timesheet export and a lookup file called cons.txt with real name on the left, fictitious name on the right which was pipe delimited:

Real Name A|Berry Kline
Real Name B|Mark Pencil
Real Name C|Admin Quincy

I picked plain text because it is easy to audit, diff, and regenerate. With both files in the working directory, the prompt to Claude was:

❯ The cons.txt file has 2 columns. The first column is the actual name.
  It is delimited by a "|". The second column is the fictious name.
  Create a new file called fictious-name.csv. Take the time.csv and
  filter out only the names from the first column. Then write out each
  row but write it out with the fictious name from the second column.

That single instruction did two jobs in one pass. Consultants not listed in cons.txt were dropped entirely, while listed ones had their names swapped to the fictitious alias. Every other field passed through untouched, and the result was written to fictious-name.csv.

Swapping Real Clients for Tech Giants with Claude

Client names are even more sensitive than consultant names, and utilization analysis needs consistent labels across the whole period. This meant that editing the file manually was not an option.

❯ From the fictious.csv, the 3rd column is the Client name. Find the
  top 25 tech companies and replace each of the company names with
  the tech company names. You must do a global search and replace
  for each company as I will be doing a utilization report and need
  to be consistent.

Claude counted thirty distinct client names in the file and pulled thirty tech company names for the substitution. The result is a CSV that looks like the original but carries none of its sensitive data.

Date,Employee,Client Name,Project,Task,Hours,Supplier,Billable / Not Billable
1-Oct-25,Berry Kline,Samsung,Samsung Monitoring Support Services,Managed Services,0.5,Snowflake,Billable
1-Oct-25,Mark Pencil,Broadcom,Data Engineering-50171304,DOT SUPPORT,8,Snowflake,Billable
1-Oct-25,Admin Quincy,Broadcom,Data Engineering-50171304,APEX SUPPORT,12,Snowflake,Billable
1-Oct-25,Sam Carson,Qualcomm,Managed Services,Month End,0.5,Snowflake,Billable

The whole redaction pipeline ran inside Claude Code without a single line of code that I wrote myself. I described the work in plain English and Claude Code produced the files.

Loading the CSV Into Oracle AI Database 26ai

Next, let's prepare our data set to be loaded into an Oracle table. Look at the prompt I gave Claude Code.

❯ Use the oracle-selectai-wallet. Create a table that maps columns to
  the fictious-time.csv file. Load the data from the csv to the table
  that you will create.

My single prompt told MCP to do three jobs: infer a schema, create the table, and load 1,084 rows. Take note that the prompt didn't provide a table name or column names. I could have been extra explicit with the context but chose to let the AI engine make the choices.

Creating the Table with Claude Code

Claude Code read the CSV header for names and sampled the first rows for types: Hours numeric with decimals, Date in the DD-MON-RR format Oracle parses natively, free text columns well under 120 chars. It returned the following DDL.

CREATE TABLE CONSULTANT_TIME (
  ENTRY_DATE   DATE,
  EMPLOYEE     VARCHAR2(60),
  CLIENT_NAME  VARCHAR2(60),
  PROJECT      VARCHAR2(120),
  TASK         VARCHAR2(80),
  HOURS        NUMBER(5,2),
  SUPPLIER     VARCHAR2(40),
  BILLABLE     VARCHAR2(12)
);

BILLABLE kept the literal source text (Billable / Not Billable) so the table mirrors the CSV. Claude sent the statement through run-sql and Oracle answered in one line.

Table CONSULTANT_TIME created.

A quick describe command below confirmed the table structure:

❯ describe consultant_time table
Name          Null?  Type
------------  -----  -------------
ENTRY_DATE           DATE
EMPLOYEE             VARCHAR2(60)
CLIENT_NAME          VARCHAR2(60)
PROJECT              VARCHAR2(120)
TASK                 VARCHAR2(80)
HOURS                NUMBER(5,2)
SUPPLIER             VARCHAR2(40)
BILLABLE             VARCHAR2(12)

A single prompt produced one round trip to the database and created a new table which completed the first third of the original ask.

First Attempt: SQLcl load

The instinctive next command was SQLcl's single line bulk loader:

❯ load CONSULTANT_TIME /Users/vendome/Projects/dbaexpert.com/fictious-time.csv

MCP answered:

SP2-0738: Restricted command:
"load CONSULTANT_TIME /Users/vendome/Projects/dbaexpert.com/fictious-time.csv"
not available

The SQLcl MCP server blocks any command that touches the local file system. That is the right default from a security standpoint, and it meant the bulk loader was off the table for this run. There are commands you are not allowed to execute through run-sqlcl. The server rejects them with SP2-0738: Restricted command:

Fallback Option #1: A Python Script Written by Claude Code

The next prompt asked Claude to shape the data for a load that worked with the MCP server.

❯ The SQLcl load command is restricted by the MCP server. Read
  fictious-time.csv, parse it as CSV (there are quoted fields with
  commas inside), and write a Python script that emits INSERT ALL
  statements in batches of 100 rows, targeting CONSULTANT_TIME. Use
  TO_DATE('...','DD-MON-RR') for the date column and escape single
  quotes.

Claude wrote the script, ran it, and produced eleven batch files under /tmp/ct_load/: ten of 100 rows and one of 84.

Fallback Option #2: SQL*Loader

The textbook fallback would have been SQL*Loader. Claude could have generated a sqlldr control file and pushed 1,084 rows with direct path speed, but that needs the Oracle client on the agent's machine. I did not have it installed and did not want the extra effort for a one-off load. Option #1 stayed with what was already on the box.

MCP Behaves Like a Heterogeneous Gateway

Oracle DBAs who have worked with DG4ODBC will recognize the behavior. MCP is not a bulk transport. Every tool call is one request paired with one response, so eleven INSERT ALL batches means eleven round trips. For 1,084 rows the overhead is invisible, but for a million rows the right answer is still SQL*Loader or an external table.

Confirming the Load

SELECT COUNT(*) FROM CONSULTANT_TIME;
  ROW_COUNT
-----------
       1084

The load produced 1,084 timesheet entries with:

Q4 Utilization: One Prompt, One Chart

With the data loaded and MCP proved out as a two-way channel between Claude and the database, I was finally able to ask the core question. What does utilization look like this quarter? This was answered with a single prompt.

❯ Show me a graphical representation of consultant utilization based
  on 500 billable hours per quarter. Sum the billable hours for
  October, November, and December. Show me the utilization as a
  percentage for each consultant. Render it as a bar chart with the
  utilization percentage labeled, ordered from highest to lowest.

Claude translated the ask into an aggregate SQL query, sent it through the run-sql MCP tool, and produced the result as an ASCII bar chart in the chat window. Here is the SQL it generated.

SELECT employee,
       SUM(hours)                            AS billable_hours,
       ROUND(SUM(hours) * 100 / 500, 2)      AS util_pct
FROM   consultant_time
WHERE  billable = 'Billable'
  AND  entry_date >= DATE '2025-10-01'
  AND  entry_date <  DATE '2026-01-01'
GROUP  BY employee
ORDER  BY SUM(hours) DESC;

It is a straightforward aggregate that filters to billable rows inside Q4, sums hours per consultant, computes the percentage against the 500-hour target, and sorts from high to low, with no hand-holding required. Here is the chart it came back with.

Consultant           Hours     Util%    Utilization (1 block = 2%)         100% target ┤
───────────────────  ───────   ───────  ─────────────────────────────────────────────────
Admin Quincy          669.00   133.80%  ███████████████████████████████████████████████████████████████████  ← OVER
Berry Kline           502.50   100.50%  ██████████████████████████████████████████████████▏
Randy Rogers          498.00    99.60%  █████████████████████████████████████████████████▊
Aaron Thomas          473.50    94.70%  ███████████████████████████████████████████████▍
Charlie Handsome      472.50    94.50%  ███████████████████████████████████████████████▎
Mark Pencil           400.00    80.00%  ████████████████████████████████████████
Sam Carson            216.50    43.30%  █████████████████████▊
Joe Cool              157.55    31.51%  ███████████████▊
Matthew Shark         127.00    25.40%  ████████████▊
───────────────────  ───────   ───────  ─────────────────────────────────────────────────
                                        0%                                            100%

Quarter:  Oct 1 to Dec 31, 2025        Target: 500 billable hours per consultant
Total Q4 billable hours (9 consultants): 3,516.55      Average utilization: 78.12%

Now, I will ask for a graphical representation with the following prompt.

❯ I want a white background. Show me a graphical representation of the
  consultant's utilization in a dashboard with each of the consultant
  in a ring chart with percentage of utilization and total number of
  hours. Also show me a bar chart with the same data points side by side.

Claude Code built the dashboard and returned a link to the rendered HTML page. It is a live page you can open right now.

See it live (click here)
Q4 2025 Consultant Utilization Dashboard →

Rendered by Claude Code in a single session. No ETL, no BI tool, no manual SQL.

With MCP in place, we built an analytical dashboard without an ETL job in the pipeline and without a BI tool to host the chart. The data came out of Oracle AI Database 26ai through SQLcl, the dashboard rendered as a browser page, and the entire loop stayed inside one Claude Code session.

Claude Code did not stop at the chart. It read back the numbers it had just produced and offered its own assessment of what the quarter said about each consultant. You can see that read in the Observations section below.

Observations (from Claude)

All the setup from Part 1 paid off here in a single prompt. There was no ETL job and no BI tool involved. A single chat message produced an aggregate against 1,084 rows and a chart that a practice lead could act on.

Conclusion

With MCP in place, you can simply describe what you want in plain natural English and get real output from the database, whether the question is about a schema, a table, or almost anything the data dictionary knows about. You can ask Claude to write stored procedures, packages, and functions, and you can use the same chat window to investigate database size, tablespace usage, security posture, or to copy a schema from one environment to another. English, paired with the right prompt and the right context engineering, is becoming the new programming language for database work. Come back for Part 3, where we shift the same MCP plumbing toward the data architect's view of the database: extending the schema, auditing the design, indexing the foreign keys, and producing a complete ER diagram in a single prompt.