Natural Language SQL with Oracle MCP and LangChain4j

Want to build agentic applications with MCP integrations in Java? Sure! Just use Langchain4j.

In this article, we’ll build a natural language interface for your database orchestrated by Langchain4j agents, integrating Oracle’s SQLcl MCP server (Model Context Protocol).

Diagram illustrating the interaction between a user prompt, SQLcl MCP Agent, Oracle Database Free, LLM Chat APIs, and a Query Report Agent, with Java and a parrot icon.

The app takes natural-language questions from a terminal prompt, generates the appropriate SQL, executes it through SQLcl using the MCP protocol, and then summarizes the results into a clean report using a second agent. You can extend this pattern into more advanced database-aware AI applications.

Prerequisites

Start the Oracle Database Free Container

We’ll use an Oracle AI Database Free container for our Langchain4j application. Clone the mcp-agent module containing the docker compose script.

Nexdt, start the Oracle Database Free container with Docker Compose. This will also run the initialization script (oracle/grant_permissions.sql) to create the testuser schema, create sample tables (players, games, game_sessions), and insert sample data.

docker compose up -d

The database will be available at localhost:1530/freepdb1 with admin password Welcome12345. The sample user is testuser/testpwd.

Configure SQLcl for MCP agent access

We’ll use SQLcl as an MCP server for natural language access to Oracle AI Database.

If you don’t have SQLcl already, download it here and unzip the package on your system. You can start SQLcl using the bin/sql file in the downloaded package. Ensure the SQLcl sql binary is on your PATH before continuing.

To configure SQLcl for the Langchain4j app, connect to the container database with the created app user:

sql  testuser/testpwd@localhost:1530/freepdb1

Then, save the connection for command line MCP access. Langchain4j app agents use the saved SQLcl connection:

conn -save cline_mcp -savepwd testuser/testpwd@localhost:1530/freepdb1

Build and run the app

Note: the app uses OpenAI’s chat models. If you don’t have an OpenAI API Key, create one now.

Once you have your OpenAI API Key, set it in your shell environment:

OPENAI_API_KEY="Your OpenAI API Key"

From the mcp-agent directory, compile and run the application:

mvn clean compile exec:java

After the app starts, you’ll see a simple terminal prompt where you can talk to your database:

Starting Oracle SQLcl MCP Agent Example
Enter text (type 'exit' to quit):
>

Prompts sent to this interface are processed by the Langchain4j app and its agents. Let’s try it out!

Ask your database natural language questions

The pre-configured sample data includes games, players, and game sessions as defined in the grant_permissions.sql script – you may wish to inspect this schema before asking questions!

Using the terminal input, we can ask questions like “Show me the top 10 players by score”. You should see an output similar to the following:

> Show me the top 10 players by score
########### PROCESSING ###########
### Agent runQuery completed
### Agent writeReport completed

###### SQL Query Results Report: Top 10 Players by Score

The following report details the top 10 players based on their total scores:

1. **Player7**: 51,062
2. **Player37**: 45,476
3. **Player27**: 44,950
4. **Player14**: 44,923
5. **Player35**: 40,831
6. **Player43**: 38,871
7. **Player24**: 38,850
8. **Player18**: 37,783
9. **Player26**: 34,451
10. **Player49**: 33,339

Try experimenting with your own prompts. The app uses the runQuery and writeReport agent methods to process your input and generate a response.

When you’re done, type exit to quit (or use Ctrl+C).

Exploring the Langchain4j Implementation

Let’s now walk through the Langchain4j agent implementation. You can find the full code here: MCP Agent code.

MCP Agent Tool

Because our agents use the SQLcl MCP server, we need to define a Langchain4J ToolProvider that gives them access. ToolProviders give agents access to different capabilities, like MCP.

The SQLclMCPToolProvider class creates a ToolProvider using LangChain4j builders. In this case, the ToolProvider allows command-line access to SQLcl with the StdioMCPTransport protocol:

package com.example.mcp;

import dev.langchain4j.mcp.McpToolProvider;
import dev.langchain4j.mcp.client.DefaultMcpClient;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.transport.McpTransport;
import dev.langchain4j.mcp.client.transport.stdio.StdioMcpTransport;

import java.util.List;

public class SQLclMCPToolProvider {
    /**
     * Creates a McpToolProvider for a SQLcl MCP client
     * using the Stdio transport protocol.
     * @return A configured McpToolProvider.
     */
    public static McpToolProvider create() {
        McpTransport transport = new StdioMcpTransport.Builder()
                .command(List.of("sql", "-mcp"))
                .logEvents(true) // only if you want to see the traffic in the log
                .build();

        McpClient client = new DefaultMcpClient.Builder()
                .key("SQLclClient")
                .transport(transport)
                .build();

        return McpToolProvider.builder()
                .mcpClients(client)
                .build();
    }
}

ToolProviders created this way can be used with any Langchain4j agent.

Agents

Our program defines two main agents, the SQLclMCPAgent and the SQLSummaryAgent.

  • The SQLclMCPAgent takes user prompts and invokes the SQLcl MCP tool to run a database query.
  • The SQLSummaryAgent takes query results and turns them into clear, concise summaries.

Both agents provide an interface using Langchain4j @UserMessage and @Agent annotations. The SQLclMCPAgent is configured like so:

package com.example.mcp;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface SQLclMCPAgent {

    @UserMessage("""
    You are a natural-language SQL agent connected to an Oracle SQLcl MCP Server
    with the connection name {{dbConnection}}.
    Your task is to understand the user’s question, determine the correct SQL needed,
    and use the MCP SQL execution tool to run that SQL.
    
    • Accept natural language requests such as “show me daily revenue” or
      “list the top 10 customers by sales.”
    • Translate the request into valid, safe Oracle SQL.
    • Call the SQLcl MCP tool to execute the query.
    • Return the results in a clear, concise, human-readable format.
    • Never guess schema details—first inspect or query metadata if needed.
    • Do not perform destructive operations (no DROP, DELETE, TRUNCATE, or DDL).
    • Prioritize correctness, safety, and interpretability.
    
    The user is asking for a SQL-backed answer. Interpret their message accordingly.
    The user query is "{{queryText}}".
    """)
    @Agent(description = "Run SQL queries")
    String runQuery(@V("queryText") String queryText, @V("dbConnection") String dbConnection);
}

Next, the SQLSummaryAgent summarizes the results of the SQLclMCPAgent into a clean, direct report:

package com.example.mcp;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface SQLSummaryAgent {

    @UserMessage("""
        You are a professional editor who reviews and rewrites results from a SQL MCP server.
        Summarize the provided SQL query results as a text report.
        Prioritize a clean, direct report that identifies key details.
        The SQL query results are "{{queryResults}}".
        """)
    @Agent(outputKey = "report", description = "Rewrites SQL query results as text reports")
    String writeReport(@V("queryResults") String queryResults);
}
Agent instrumentation (main class)

The MCPAgentApplication class wires the SQLcl and reporting agents together with a top-level sequencing agent. We supply the sequencing to the TerminalInput class for a simple text interface.

The SQLclMCPAgent and SQLSummaryAgent interfaces are initialized with the AgenticServices.agentBuilder Langchain4j builder. The agentBuilder provides methods to supply tools, chat memory, and other agentic constructs.:

package com.example.mcp;

import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.agentic.UntypedAgent;
import dev.langchain4j.agentic.agent.AgentResponse;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;

import java.util.function.Consumer;

public class MCPAgentApplication {
    public static void main(String[] args) {
        ChatModel chatModel = OpenAiChatModel.builder()
                .apiKey(System.getenv("OPENAI_API_KEY"))
                .modelName("gpt-4o-mini")
                .build();

        Consumer<AgentResponse> agentCompletedLogger = agentResponse -> {
            System.out.printf("### Agent %s completed ###", agentResponse.agentName());
        };

        // Use an in-memory chat memory
        ChatMemory chatMemory = new MessageWindowChatMemory.Builder()
                .id("12345")
                .maxMessages(10)
                .chatMemoryStore(new InMemoryChatMemoryStore())
                .build();

        SQLclMCPAgent sqLclMCPAgent = AgenticServices.agentBuilder(SQLclMCPAgent.class)
                .chatModel(chatModel)
                .chatMemoryProvider((any) -> chatMemory)
                .afterAgentInvocation(agentCompletedLogger)
                .toolProvider(SQLclMCPToolProvider.create())
                .outputKey("queryResults")
                .build();

        SQLSummaryAgent sqlSummaryAgent = AgenticServices.agentBuilder(SQLSummaryAgent.class)
                .chatModel(chatModel)
                .afterAgentInvocation(agentCompletedLogger)
                .outputKey("report")
                .build();

        UntypedAgent topLevelAgent = AgenticServices
                .sequenceBuilder()
                .subAgents(sqLclMCPAgent, sqlSummaryAgent)
                .outputKey("report")
                .build();


        System.out.println("Starting Oracle SQLcl MCP Agent Example");
        new TerminalInput(topLevelAgent).run();
    }
}

That’s it! You can take this example and extend it to various agent use-cases, with Oracle and beyond.

References

Response

  1. […] 🤖 Natural Language SQL with Oracle MCP and LangChain4j – Anders Swanson shows how you can build agentic applications with MCP, Oracle Database and […]

Leave a Reply

Discover more from andersswanson.dev

Subscribe now to keep reading and get access to the full archive.

Continue reading