mle-cli
v1.0.1
Published
Oracle MLE cli tool for development and testing. Run and test your JavaScript straight from the command line
Readme
mle-cli
A fast, no-fuss CLI for Oracle’s Multilingual Engine (MLE). Use JavaScript in your Oracle database right from the terminal—no complex setup required.
At a glance
- Run JavaScript in your Oracle database straight from the terminal
- Interactive REPL
- In-database test runner with readable results
- ORDS endpoint scaffolding to expose REST using only JavaScript
- Manage MLE objects (modules, environments)
- Optional auto-bundling (TypeScript and npm dependencies)
🧠 Note: Under the hood
mle-cliuses node-oracledb to connect to your database instance and execute generated queries.
Documentation
This README is the documentation for mle-cli. It covers installation, configuration, command usage, and examples.
For more detail on tests and REST endpoints, see:
- Unit Test Writing Guide — assertions, suites, lifecycle hooks, and test selection.
- ORDS Endpoint Library Guide — routes, request handlers, and responses.
Examples
Each command description in Available commands includes usage and examples.
Prerequisites
Before getting started, you'll need:
- An instance of Oracle 26ai that you can connect to
- A database user with the following privileges (granted individually or through the
DB_DEVELOPER_ROLErole)CREATE SESSIONEXECUTE DYNAMIC MLECREATE PROCEDURECREATE MLE
- A JavaScript runtime (Node.js) installed locally
The tool primarily supports connecting using TCP/IP, which should cover most use cases. For additional connection options, see the configuration section below.
If you don't have Node.js installed, you can download it from: https://nodejs.org/en/download
Installation
You can install mle-cli using npm by running one of the following commands:
Local installation (recommended for project-specific use):
npm install mle-cliLocal installation as a development dependency:
npm install --save-dev mle-cliGlobal installation (for use from any directory):
npm install --global mle-cliTo update a local installation:
npm update mle-cliTo update a global installation:
npm update --global mle-cli🧠 Note: For local installations, prefix the commands in this README with
npx. So using it would look like this:npx mle-cli run /path/to/file.js
Configuration
⚠️ WARNING ⚠️ Storing passwords in plaintext is not safe.
To connect to your database, mle-cli can assemble connection settings from several sources. For the same field, the effective priority is:
--connect/-c- A config file passed with
--config/-f - A config file found under
$HOME/.mlecli - Environment variables for any still-missing supported fields
A few details matter:
- When
--connectis present, config files are not loaded. - Environment variables only fill missing
username,connectString, andwalletLocationvalues. They do not override values already supplied by--connector a config file. - The interactive password prompt is only used with
--connect. Config files and environment variables are non-interactive.
For simple username/password logins, provide username and connectString. For wallets, access tokens, or other advanced node-oracledb connection attributes, use a config file.
Connection string on the command line
Use --connect (-c) for easy connect strings:
$ mle-cli -c 'scott@server:port/serviceName' run file.js
Please enter password: You can also add a privilege suffix such as AS SYSDBA:
$ mle-cli -c 'sys@server:port/serviceName as sysdba' repl
Please enter password:Supported privilege suffixes are SYSASM, SYSBACKUP, SYSDBA, SYSDG, SYSKM, SYSOPER, SYSPRELIM, and SYSRAC.
Note: Do not include your password in the connect string. If you pass user/password@..., mle-cli ignores the embedded password and still prompts for it. When --connect is used, config files are skipped, so use a config file for walletLocation, accessToken, externalAuth, or other node-oracledb connection attributes.
Config files
mle-cli loads top-level connection attributes from JSON or JavaScript config files. Supported file types are .json, .js, .cjs, and .mjs.
Basic examples:
Username/password authentication also requires a password connection attribute. Config files do not trigger a password prompt.
The examples omit credentials. For an interactive password prompt, use --connect.
JSON
{
"username": "my_user",
"connectString": "mydbmachine.example.com/orclpdb1"
}JavaScript (ESM, config.mjs)
export default {
username: "my_user",
connectString: "mydbmachine.example.com/orclpdb1",
}JavaScript (CJS, config.cjs)
module.exports = {
username: "my_user",
connectString: "mydbmachine.example.com/orclpdb1",
};Since the tool accepts JavaScript source as a configuration option it is also possible to use configuration providers from node-oracledb.
Full connect descriptors are also supported
{
"username": "my_user",
"connectString": "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=mymachine.example.com)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orcl)))"
}The CLI tool will automatically search these exact paths for configuration files:
$HOME/.mlecli/config.json$HOME/.mlecli/config.js$HOME/.mlecli/config.cjs$HOME/.mlecli/config.mjs
No project-local config search is performed. If both JSON and JS configuration files are present in $HOME/.mlecli, mle-cli will prioritize the JSON configuration file.
🧠 Note: The CLI tool passes configuration values along to node-oracledb. This means that all options available in node-oracledb are also available to
mle-cli. If your config includesaccessToken,mle-cliautomatically enablesexternalAuth. For more see the node-oracledb documentation.
Specifying a custom config file:
You can tell mle-cli the exact path of your configuration file to bypass the default $HOME/.mlecli search:
$ mle-cli --config path/to/config.json repl
>Or use the short form:
$ mle-cli -f path/to/config.json repl
>💡 Tip: Relative paths are resolved from your current working directory. You can place the
--configflag anywhere in your command:$ mle-cli run --config path/to/config.json hello.js hello world
Environment variables
If username, connectString, or walletLocation are still missing after --connect or config-file processing, mle-cli will attempt to retrieve them from the following environment variables:
| Variable | Description |
| --------------------- | -------------------------------------- |
| MLECLI_USERNAME | Database username |
| MLECLI_CONN_STRING | Connection string (host/service) |
| MLECLI_WALLET_LOC | Path to wallet directory |
These variables are fallback values only; they do not override CLI or config-file values.
Note: Password values are intentionally not supported through environment variables.
Import maps
Some commands of this tool accept import maps. These are used to describe environments and the tools will create MLE environments based on the import map when these are given.
Example
$ mle-cli run --import-map example-map.json file-to-run.jsThese import maps describe import names and the MLE module these map to.
Example
{
"imports": {
"my-imported-module": "MOD_NAME",
"other-module": "OTHER_MODULE_NAME"
}
}With --env, the command uses mappings from an existing MLE environment:
$ mle-cli run --env MY_ENV imports.jsAvailable commands
This section covers each command in detail, including the interactive REPL, test runner, and options for managing MLE objects.
Use mle-cli <command> --help to see the options for a specific command.
$ mle-cli --help
Usage: mle-cli [options] [command]
Oracle MLE cli tool for development and testing. Run and test your JavaScript straight from the command line
Options:
-V, --version output the version number
-f, --config <path> path to configuration file for connection information.
-c, --connect <info> connection info as an easy connect string in the format {user}@{connect string}[ AS {privilege}].
--debug run in debug mode.
--experimental <feature> enable the specified experimental feature (choices: "build")
-h, --help display help for command
Commands:
run [options] <path> Run the given js file with MLE.
repl [options] Start a Read-Eval-Print-Loop using MLE.
create Create MLE objects.
drop Drop MLE objects.
list List MLE related database objects.
test [options] [path] Run your unit tests in the database using MLE.
help [command] display help for command
Environment variables supported:
MLECLI_USERNAME - Database username (alternative to --connect or config)
MLECLI_CONN_STRING - Database connect string (alternative to --connect or config)
MLECLI_WALLET_LOC - Wallet location for connections (alternative to --connect or config)
If both CLI flags and environment variables are provided, flag values take precedence.run
Usage: mle-cli run [options] <path>
The run command executes a local JavaScript file:
$ cat hello.js
console.log('hello world');
$ mle-cli run hello.js
hello worldIf your code contains errors, mle-cli reports file paths and line numbers. In this example, errorstack.js contains:
function fail() {
throw new Error('This is a useful error msg.');
}
fail();$ mle-cli run errorstack.js
Error: This is a useful error msg.
at fail errorstack.js (line: 2, column: 11)
at errorstack.js (line: 5, column: 1)MLE Environments
You can run your JS files in specific MLE environments. Please refer to the Import maps section to see how to do this.
Auto-bundling
Internally, mle-cli invokes esbuild on input files, with bundling enabled. Since, esbuild supports TypeScript type erasure this means that you can give TypeScript files to run with imports from other files or your node_modules and it will still work.
Unfortunately, MLE doesn't support source maps so the original source locations will not map back and forth (e.g. in error stacks). For this reason this feature is behind the experimental build flag.
import { PackageURL } from 'packageurl-js';
function doThing(url: string) {
const purl = PackageURL.fromString(url);
console.log(purl.name);
console.log(purl.namespace);
console.log(purl.type);
console.log(purl.version);
}
doThing('pkg:maven/org.springframework.integration/[email protected]');$ mle-cli --experimental build run purl.tsOutput
spring-integration-jms org.springframework.integration maven 5.5.5
How it works
The run command reads the contents of the provided file and creates an MLE module[^1]. Then an environment that maps the newly created MLE module is created. Afterwards we force the evaluation of the module by importing it using DBMS_MLE. The output streams (stdout/stderr) are captured in a CLOB along with any exception information if there is any (message and stack trace). In the end the module is dropped and the client formats and prints the result of the evaluation.
This approach enables several advanced features:
- Top-level await support:
console.log(await Promise.resolve('a value'));- Static ESM imports: This example assumes
MY_MODULEandMY_ENVexist as described undercreate. The local fileimports.jscontains:
import { sayHello } from 'my-module';
console.log(sayHello());$ mle-cli run --env MY_ENV imports.js
hello world[^1]: To create an MLE module it first creates a "holder" PL/SQL package with a BLOB variable and a function that returns the value of the variable. Then the variable is written with the contents of the file. The MLE module is created by selecting the function, which returns the BLOB. Finally the "holder" package is dropped.
repl
Usage: mle-cli repl [--env <name>]
REPL (Read-Eval-Print-Loop) provides an interactive shell where you can type JavaScript commands and see results immediately:
$ mle-cli repl
> 2 + 2
4
> .exitMulti-line input is also supported:
$ mle-cli repl
> console.log(`
... My multi-line string.
... Has a second line
... `);
My multi-line string.
Has a second line
> function f1() {
... console.log('Hello World');
... }
> f1();
Hello WorldYour command history is saved at $HOME/.mle_repl_history. You can navigate through previous commands using the up and down arrow keys or use reverse-i-search to find specific commands.
.exit- Exit the REPL.editor- Enter editor mode for multi-line input.save path/to/file- Save the current REPL session to a file.load path/to/file- Load commands from a file into the current session
🟡 The following special commands are NOT currently supported:
.break.clear
Working with async code and imports
Unlike the run command, the REPL doesn't support top-level await. To use async features, wrap your code in an immediately-executed async function:
$ mle-cli repl
> (async () => {
... console.log(await Promise.resolve('hello'));
... })();
hello
> .exitFor importing modules, use dynamic imports:
$ mle-cli repl
> (async () => {
... const { fetch } = await import('mle-js-fetch');
... fetch('http://example.org');
... })();💡 Tip: To make imported symbols available for later use in the REPL session, assign them to the global object:
> (async () => { ... const { fetch } = await import('mle-js-fetch'); ... globalThis.fetch = fetch; ... })(); > fetch('http://example.org');For modules with many exports:
> (async () => Object.assign(globalThis, await import('mle-js-fetch')))(); > fetch('http://example.org');
How it works
The CLI uses REPLServer from node:repl to provide the interactive interface.
When the server is started, a standalone connection to the database is created along with a DBMS_MLE context. The handle to that context is sent back to the client and used later.
The custom evaluator provides multiline input buffering (using node to detect when a unit is complete) and the executor. When a complete code unit is detected it is sent to the database for execution using DBMS_MLE. The output streams (stdout/stderr) are captured in CLOBs and sent back to the client along with exception information (if one occured).
create
You can create MLE objects directly from the command line. This command has multiple subcommands, one for each object type.
module
Usage: mle-cli create module <path> --name <name> [--no-replace]
The module subcommand installs a local JavaScript file as an MLE module in the database. The specified name is case sensitive.
In this example, source.js contains:
export function sayHello() {
return 'hello world';
}The following command installs source.js as MY_MODULE:
$ mle-cli create module ./source.js --name MY_MODULEBy default, the command replaces an existing module with the same name. With --no-replace, the command fails if the module already exists.
$ mle-cli create module ./source.js --name MY_MODULE --no-replaceFor the same source in source.ts, the command enables auto-bundling:
$ mle-cli --experimental build create module ./source.ts --name MY_MODULEenv
Usage: mle-cli create env <path> --name <name> [--no-replace]
The env subcommand creates an MLE environment from an import map. The environment name is case sensitive.
This example assumes MY_MODULE already exists and my-env.json contains the following import map:
{
"imports": {
"my-module": "MY_MODULE"
}
}Example:
$ mle-cli create env --name MY_ENV ./my-env.jsonBy default, the command replaces an existing environment. Use --no-replace to fail if the name already exists.
ords
Usage: mle-cli create ords <path> --name <name> [--env <name> | --import-map <path>] [--print]
The ords subcommand installs a JavaScript file that exports ORDS handlers.
It creates the MLE module, an MLE environment, and the ORDS modules, templates, and handlers.
The CLI also installs its bundled mle-cli/ords runtime library.
This command requires ORDS in the database and a REST-enabled schema.
In this example, hello-ords.js exports the following handler:
import { ords } from 'mle-cli/ords';
export const getHello = ords.get('/hello', (req, resp) => {
resp.json({ message: 'Hello, ORDS!' });
});The following command installs the endpoint:
$ mle-cli create ords hello-ords.js --name HELLO_ORDSWith --print, the command prints the generated ORDS SQL:
$ mle-cli create ords hello-ords.js --name HELLO_ORDS --print--print still connects to the database and creates or replaces MLE objects. It skips execution of the generated ORDS SQL.
For handler definitions, see the ORDS Endpoint Library Guide.
drop
You can drop MLE objects directly from the command line. This command has multiple subcommands, one for each object type.
module
Usage: mle-cli drop module <name>
Drops the module with the given name. Please note that names are case sensitive.
$ mle-cli drop module MY_MODULEenv
Usage: mle-cli drop env <name>
Drops the environment with the given name. Please note that names are case sensitive.
$ mle-cli drop env MY_ENVlist
You can list MLE objects in the schema of your user.
modules
Usage: mle-cli list modules
List MLE modules. Example output for two modules:
$ mle-cli list modules
MODULE_NAME VERSION TYPE
----------- ------- ----
MOD1 1.3.2 TEXT
MOD2 0.2.3 TEXTenvs
Usage: mle-cli list envs
List MLE environments. Example output for an environment with two import mappings:
$ mle-cli list envs
Environment: TEST_ENV
mod-1 -> MOD1
mod-2 -> MOD2test
Usage: mle-cli test [options] [path]
mle-cli test runs both test cases and assertions inside Oracle Database, using MLE.
Tests can import MLE modules and execute SQL through the built-in session object.
The CLI handles that execution and reports the results in your terminal.
The test runner accepts a test file or searches a directory recursively. With no path, it searches the current directory.
It discovers *.test.js, *.test.mjs, *.test.ts, and *.test.mts files. TypeScript files require --experimental build.
The command returns a non-zero exit status for failed tests or load errors.
In this example, test/example.test.js contains one test:
import { test, expect } from 'mle-cli/test';
test('addition', () => {
expect.equal(2 + 2, 4);
});The following command runs all tests under test inside the database:
$ mle-cli test ./testThe output includes the test result and an overall summary:
Test "addition" - passed
...
Suite summary: 1 passed, 0 failed, 0 skipped.For a single test file:
$ mle-cli test ./test/example.test.jsWithout a path, the command searches the current directory:
$ mle-cli testOutput
/Users/myuser/Development/mle-project/test/example1.test.js -------------------------------------------------------------------------------- Test "My first test" - passed Test "My failing test" - failed AssertionError: Expected value to be true, but evaluated to false. at expect.true (mle-cli/test:169:13) at example1.test.js:8:12 at TestContext.execute (mle-cli/test:112:21) at async RootContext.execute (mle-cli/test:22:41) Suite: "First suite" Test "My first test" - passed Test "My failing test" - failed AssertionError: Expected value to be true, but evaluated to false. at expect.true (mle-cli/test:169:13) at example1.test.js:17:16 at TestContext.execute (mle-cli/test:112:21) at async SuiteContext.execute (mle-cli/test:69:38) at async RootContext.execute (mle-cli/test:22:41) File summary: 2 passed, 2 failed, 0 skipped. (~2 ms) -------------------------------------------------------------------------------- /Users/myuser/Development/mle-project/test/some-test-suite/example2.test.js -------------------------------------------------------------------------------- Suite: "My module" Test "My first test" - failed AssertionError: Expected "2" to be equal to "3". at expect.equal (mle-cli/test:179:13) at example2.test.js:5:16 at TestContext.execute (mle-cli/test:112:21) at async SuiteContext.execute (mle-cli/test:69:38) at async RootContext.execute (mle-cli/test:22:41) Test "My failing test" - passed File summary: 1 passed, 1 failed, 0 skipped. (~0 ms) -------------------------------------------------------------------------------- ================================================================================ Suite summary: 3 passed, 3 failed, 0 skipped. ================================================================================
Searching a specific directory:
$ mle-cli test ./test/some-test-suiteOutput
/Users/myuser/Development/mle-project/test/some-test-suite/example2.test.js -------------------------------------------------------------------------------- Suite: "My module" AssertionError: Expected "2" to be equal to "3". at expect.equal (mle-cli/test:179:13) at example2.test.js:5:16 at TestContext.execute (mle-cli/test:112:21) at async SuiteContext.execute (mle-cli/test:69:38) at async RootContext.execute (mle-cli/test:22:41) Test "My first test" - failed Test "My failing test" - passed File summary: 1 passed, 1 failed, 0 skipped. (~1 ms) -------------------------------------------------------------------------------- ================================================================================ Suite summary: 1 passed, 1 failed, 0 skipped. ================================================================================
If the path is a single file, then only that file will be executed:
$ mle-cli test ./test/example1.test.jsOutput
/Users/myuser/Development/mle-project/test/example1.test.js -------------------------------------------------------------------------------- Test "My first test" - passed AssertionError: Expected value to be true, but evaluated to false. at expect.true (mle-cli/test:169:13) at example1.test.js:8:12 at TestContext.execute (mle-cli/test:112:21) at async RootContext.execute (mle-cli/test:22:41) Test "My failing test" - failed Suite: "First suite" Test "My first test" - passed AssertionError: Expected value to be true, but evaluated to false. at expect.true (mle-cli/test:169:13) at example1.test.js:17:16 at TestContext.execute (mle-cli/test:112:21) at async SuiteContext.execute (mle-cli/test:69:38) at async RootContext.execute (mle-cli/test:22:41) Test "My failing test" - failed File summary: 2 passed, 2 failed, 0 skipped. (~2 ms) --------------------------------------------------------------------------------
This tool includes a unit testing library called 'mle-cli/test'. It provides all the functionality you need to run and write your tests. While it's designed to work smoothly with the test runner, it's entirely optional. If you want to use a different library see the docs on using a custom unit test library.
For guidance on writing tests with 'mle-cli/test', see the API documentation.
MLE Environments
You can run your test files in specific MLE environments. Please refer to the Import maps section to see how to do this.
How it works
We use a local file system search algorithm to discover all eligible test files. At the start, we also read the contents of the bundled unit test library and create an MLE module for it. For each test file found, we:
- Read its contents.
- Create an MLE module for the test file.
- Set up an MLE environment that includes both the unit test library module and the test file module.
- Create a
DBMS_MLEcontext and import the test module, which triggers its evaluation.
The test library exports result counts and timing through MLE bindings. The CLI retrieves these results and the captured output to print summaries. After execution, the CLI removes the temporary modules and environments.
Help
Missing a feature? Something not working? Please open an issue in our repository.
Contributing
This project welcomes contributions from the community. Before submitting a pull request, please review our contribution guide.
Security
Please consult the security guide for our responsible security vulnerability disclosure process.
License
Copyright (c) 2026, Oracle and/or its affiliates.
Released under the Universal Permissive License v1.0 as shown at https://oss.oracle.com/licenses/upl/.
