CoraDB MCP Server
OfficialClick on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@CoraDB MCP Server How many orders are in the e-commerce database?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
CoraDB MCP Server
An MCP server for CoraDB. Any MCP client — an
AI app such as Claude, an IDE, a script or another program — can use it to read
a database's schema, run SELECTs
(relational and SELECT … FROM MATCH graph queries) and run other statements,
and get back exactly what the engine returns.
Stateless. Every call names its database and carries its own login. The server opens a connection for the call, runs it, commits or rolls back, closes the connection and forgets the login. Nothing is kept between calls: no connection, no session, no password.
One transaction per call. All statements of a call succeed and are committed together, or nothing is saved.
The server setting has the last word.
CORADB_WRITE_MODEdecides whether changes can be saved at all; a call can only be stricter.
Quick start
Needs Node.js 20.6 or later and a running CoraDB.
Nothing to install by hand: MCP clients start the server with npx, which
downloads @coradb/coradb-mcp-server
from npm the first time.
For the Claude desktop app: Settings → Developer → Edit Config, add this to
mcpServers, then quit the app completely and reopen it:
"coradb": {
"command": "npx",
"args": ["-y", "@coradb/coradb-mcp-server"]
}Then call a tool, for example execute_select with
{ "database": "e-commerce", "sql": "SELECT COUNT(*) FROM orders" }, or ask
the AI in plain words: "How many orders are in e-commerce?"
After the server is updated, restart the client and start a new conversation: a conversation keeps the tool definitions it started with.
Contents: Quick start · How a call works · Tools · Transactions and write mode · Results · Using it from an AI app · Calling it directly · Settings · Installing and updating · Security · Development
Related MCP server: 多数据库 MCP Server
How a call works
Every database tool takes these login inputs:
Input | Required | Default |
| yes | — |
| no |
|
| no |
|
| no |
|
| no | empty |
Only host and port can be changed in the settings. Users and passwords are never stored by the server: a call that needs another user sends it.
For each call the server:
connects to the broker at
host:portand logs in todatabaseasuser;runs the statements one after the other, in one transaction (auto-commit is off);
commits, if the call may commit and every statement succeeded — otherwise rolls back;
closes the connection and drops the login.
Several databases, servers or users are simply different calls. If the connection breaks, times out or the server stops part-way, CoraDB rolls back whatever was not committed.
Tools
Tool | What it does | Changes data |
Logs in and reports the login, engine version and server settings | no | |
Tables, views, vertex and edge tables with columns, keys, indexes | no | |
Runs SELECTs and returns columns and rows | never | |
Runs any statements; commits when all succeed, unless | yes | |
The optimizer's plan for a SELECT, without running it | no |
Inputs are strict: an input a tool does not know is refused (Unrecognized key) instead of being ignored, so a client with an outdated copy of a tool's
definition finds out instead of being misunderstood.
All tools that take SQL accept several statements separated by ;. They
run in order and the first error stops the call (later statements are reported
as skipped). A ; inside a string, a quoted name or a comment does not split.
If where a statement ends depends on server settings (for example a backslash
before a quote), the call is refused: send the statements one per call.
connection_info
Logs in and reports the login used (user@host:port/database, never the
password), the engine version, the round-trip time, the driver in use
(version, folder, when it was loaded, how many times it was reloaded), the
server's defaults, write mode and limits. Use it to check that a database is reachable.
{ "database": "e-commerce" }describe_schema
The objects of the database as the given user sees them, read from the live catalog: tables, views, vertex (node) tables and edge tables, with columns, types, nullability, defaults, primary and foreign keys and indexes.
Input | |
login inputs | see above |
| one table, view, vertex or edge table (case-insensitive) |
|
|
|
|
{ "database": "e-commerce", "object": "product" }VERTEX TABLE DBA.product
id INTEGER NOT NULL
name VARCHAR(64) NOT NULL
description VARCHAR(32) NOT NULL
unit_price NUMERIC(10,2) NOT NULL
units_in_stock SHORT NOT NULL
PRIMARY KEY (id)A kept copy is only handed out after the call's login succeeds.
execute_select
Runs one or more SELECTs and returns the columns and rows as the engine sends them. Each statement is first compiled by the server and refused, before anything runs, unless it is a SELECT. Nothing is ever committed.
Input | |
| one or more SELECT statements separated by |
login inputs | see above |
| rows returned per SELECT (default |
| time limit for the whole call (default |
| values for |
{
"database": "e-commerce",
"sql": "SELECT c.name, COUNT(p) AS products FROM MATCH (p:product)-[:part_of]->(c:category) GROUP BY c.name ORDER BY products DESC LIMIT 3"
}{
"login": "dba@localhost:33000/e-commerce",
"transaction": "rolled back",
"rolledBackBecause": "execute_select only reads",
"elapsedMs": 16,
"statements": [
{"statement":1,"sql":"SELECT c.name, COUNT(p) AS products FROM MATCH …","type":"SELECT","columns":[{"name":"c.name","type":"String","nullable":false,"precision":32,"scale":0},{"name":"products","type":"Int","nullable":false,"precision":10,"scale":0}],"totalRows":3,"truncated":false,"elapsedMs":15, "rows": [
["Hoodies & Sweatshirts",390],
["Tees",385],
["Jackets",362]
]}
]
}execute_sql
Runs any statements — INSERT, UPDATE, DELETE, MERGE, CREATE, ALTER, DROP,
SELECT … — in one transaction. Not offered when CORADB_WRITE_MODE=off.
Input | |
| one or more statements separated by |
login inputs | see above |
|
|
| as for |
{
"database": "e-commerce",
"sql": "UPDATE product SET units_in_stock = 40 WHERE id = 15"
}{
"login": "dba@localhost:33000/e-commerce",
"transaction": "committed",
"elapsedMs": 21,
"statements": [
{"statement":1,"sql":"UPDATE product SET units_in_stock = 40 WHERE id = 15","type":"UPDATE","affectedRows":1,"elapsedMs":12}
]
}The same call with "dryRun": true returns "transaction": "rolled back",
"rolledBackBecause": "dryRun was requested" and still reports
"affectedRows": 1: what would have changed. COMMIT and ROLLBACK
statements are refused — the call controls the transaction. Serial increments
(AUTO_INCREMENT, NEXT_VALUE) are not undone by a rollback; everything else,
DDL included, is.
explain_query
The plan CoraDB's optimizer chooses for each SELECT of the call, without running it (statements other than SELECT are not explained).
{ "database": "e-commerce", "sql": "SELECT c.name FROM MATCH (p:product)-[:part_of]->(c:category)" }Transactions and write mode
What happens at the end of an execute_sql call:
| Call without | Call with |
| committed if every statement succeeded, otherwise rolled back | rolled back |
| rolled back; the reply says the server allows only dry runs | rolled back |
| no | — |
A call can only be stricter than the server, never less strict. If any
statement fails, the time limit runs out or the call is cancelled, everything
is rolled back. The reply's transaction says committed or rolled back,
and rolledBackBecause says why.
Results
execute_select and execute_sql reply with one JSON document: the login,
transaction, rolledBackBecause, stopped (error, timeout or
cancelled, when the call stopped early), elapsedMs, and per statement:
type (as the server compiled it), sql, columns (name, type, precision,
scale, nullable, table), rows (one array per row), totalRows, truncated,
affectedRows, error (code and message from the engine) or skipped.
Values: numbers, strings, null, booleans. DATE as YYYY-MM-DD, DATETIME as
YYYY-MM-DD HH:MM:SS.mmm, TIMESTAMP as YYYY-MM-DD HH:MM:SS (the stored
wall-clock time, no time-zone shift). NUMERIC values that do not fit a
JavaScript number exactly come back as text. Binary values and graph objects
the driver does not decode come back as hex. BLOB/CLOB show their type and
length only. Strings longer than CORADB_MAX_CELL_CHARS are cut; rows beyond
CORADB_MAX_RESPONSE_CHARS are left out and counted in rowsOmittedForSize.
A call that stops early, and any other failure, is returned as a tool error
(isError: true): engine errors as CoraDB error <code>: <message>, an
unreachable broker as Could not work with CoraDB at host:port ….
Time limits and cancelling. Every call has a time limit (timeoutMs, or
CORADB_CALL_TIMEOUT_MS, at most CORADB_MAX_TIMEOUT_MS), also given to
CoraDB as the statement's query timeout. When it runs out, or the client
cancels the call (MCP notifications/cancelled), the running statement is
cancelled on the server, the rest are skipped and everything is rolled back.
Using it from an AI app
Claude desktop app (and Cowork). Settings → Developer → Edit Config, add
to mcpServers, then quit the app completely (also from the system tray) and
start it again. Settings go in env; leave out the ones you do not change:
"coradb": {
"command": "npx",
"args": ["-y", "@coradb/coradb-mcp-server"],
"env": { "CORADB_HOST": "localhost", "CORADB_PORT": "33000", "CORADB_WRITE_MODE": "write" }
}If the app cannot start npx on Windows, use
"command": "cmd", "args": ["/c", "npx", "-y", "@coradb/coradb-mcp-server"].
Claude Code:
claude mcp add coradb --scope user -- npx -y @coradb/coradb-mcp-serverAny other MCP client: run npx -y @coradb/coradb-mcp-server (or
coradb-mcp-server after a global install) as a stdio server; settings go in
its environment.
Ask in plain words; the AI turns the request into calls. For example:
You say | The AI calls |
"How many orders were shipped in 2025?" |
|
"Set the stock of product 15 to 40." |
|
"Check whether this update would work: …" |
|
"Is this query valid?" |
|
"Run it on demodb as reader, password …" | the same tool with |
Calling it directly
The server speaks MCP (JSON-RPC 2.0) over stdin/stdout, one JSON message per line. A program starts it, initializes, and calls tools:
→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"my-app","version":"1.0"}}}
← {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","serverInfo":{"name":"coradb",…},…}}
→ {"jsonrpc":"2.0","method":"notifications/initialized"}
→ {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"execute_select","arguments":{"database":"e-commerce","sql":"SELECT COUNT(*) FROM orders"}}}
← {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\n\"login\": …"}]}}With the MCP TypeScript SDK (Node.js):
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
const client = new Client({ name: 'my-app', version: '1.0.0' });
await client.connect(new StdioClientTransport({
command: 'npx',
args: ['-y', '@coradb/coradb-mcp-server'],
}));
const r = await client.callTool({
name: 'execute_sql',
arguments: {
database: 'e-commerce',
user: 'dba',
password: '',
sql: 'UPDATE product SET units_in_stock = 40 WHERE id = 15',
dryRun: true,
},
});
const result = JSON.parse(r.content[0].text); // { login, transaction, statements: [...] }
console.log(result.transaction, result.statements[0].affectedRows);
await client.close();scripts/smoke.mjs and test/db/live.test.mjs in the source repository are complete examples.
Settings
Environment variables, from the env block of the client's configuration.
When the server runs from a source checkout, they can also come from .env in
its folder (.env.example, included in the package, lists them all); the
environment wins over .env, and only CORADB_* variables are read from it.
Restart the client after a change. connection_info shows which came from
.env (names only).
Variable | Default | Meaning |
|
| Broker host for calls that give none |
|
| Broker port for calls that give none |
|
| Databases a call may name, comma-separated; |
|
|
|
|
| Rows per SELECT when the call does not say |
|
| Most rows a call may ask for |
|
| Most statements in one call |
|
| A call's time limit when it gives none; |
|
| Longest time limit a call may ask for |
|
| Server-side limit per statement when the call has no limit |
|
| How long to wait for the broker |
|
| Longer strings in a cell are cut |
|
| Rows beyond this size are left out of a reply |
|
| How long a schema read from the catalog is kept |
Installing and updating
With npx (recommended): nothing to install. npx downloads the package the first time and may keep using that copy; to always start the newest version use
@coradb/coradb-mcp-server@latestinargs, and to pin one, for example@coradb/coradb-mcp-server@1.0.0.Global install:
npm install -g @coradb/coradb-mcp-server, then use"command": "coradb-mcp-server"with noargs. Update withnpm update -g @coradb/coradb-mcp-server.The CoraDB driver is the npm package
@coradb/node-coradb, installed with the server. When its files change (an update), the server loads the new version before the next call, with no restart. If the new code does not load, the previous version keeps working andconnection_infoshows the error underdriver.reloadError;connection_infoalso shows the driver's version, folder, load time and number of reloads.
Security
No stored logins. The server keeps no users or passwords, in files or in memory between calls; each call brings its own and it is dropped when the call ends. Replies and
connection_infonever show passwords.The client holds the credentials. Whoever calls the server sends the password. When the caller is an AI app, the AI sees the password it sends.
CoraDB's rights apply. A call can do exactly what its user may do in that database. Use a user with only SELECT rights for reading.
Guards.
execute_selectandexplain_querylet the server compile each statement and refuse anything that is not a SELECT before it runs.COMMIT/ROLLBACKstatements are refused.CORADB_WRITE_MODE=dryrunoroffprevents saving whatever a call asks.Local only. The server talks to its client over stdio on the same computer; it opens no network port of its own.
Development
git clone https://github.com/apsisware/coradb-mcp-server.git && cd coradb-mcp-server
npm install # installs and builds dist/
npm test # unit tests, no database needed
npm run test:db # live tests against e-commerce on localhost:33000 as dbaTo run a local build from a client, use "command": "node" with
"args": ["<path to the checkout>/dist/index.js"].
Task | What it does |
| Compiles |
| Unit tests ( |
| Live tests ( |
| Calls every tool once and prints the replies ( |
| Builds and runs the unit tests first ( |
Files
File | |
| Starts the server (stdio) |
| The tools and their inputs |
| Runs a call's statements: type checks, time limits, cancelling, commit or rollback, results |
| Login resolution and the one-connection-per-call wrapper |
| Loads |
| Settings and defaults |
|
|
| Reading and printing the schema |
| Splitting a script into statements |
| Turning values into JSON |
| Unit tests (no database; |
| Live tests against e-commerce |
|
|
Available Tools
5 toolsconnection_infoCoraDB connection infoARead-only
Log in to a CoraDB database and report the login used (never the password), the engine version and this server's settings. Use it to check that a database is reachable with a given login.
Inputs: database (required), host, port, user, +1 more.
describe_schemaDescribe the CoraDB schemaARead-only
The tables, views, vertex (node) tables and edge tables of a database with their columns, keys and indexes, read from the live catalog as the given user sees it. Give object for a single table. Read this before writing queries.
Inputs: database (required), host, port, user, +4 more.
execute_selectRun CoraDB SELECT statementsARead-only
Run one SELECT, or several separated by ;, and return the columns (name, type, precision, scale) and rows exactly as the engine returns them. Works for relational SQL and graph queries (SELECT … FROM MATCH …). Each statement is compiled first and refused unless it is a SELECT; nothing is ever committed. Statements run in order and the first error stops the script.
Inputs: sql (required), database (required), host, port, +5 more.
execute_sqlRun CoraDB statementsADestructive
Run any statements — INSERT, UPDATE, DELETE, MERGE, CREATE, ALTER, DROP, SELECT … — one or several separated by ;, in order, in one transaction. If every statement succeeds, the changes are committed; if any fails, nothing is saved. dryRun: true runs everything and then rolls it back, to see what would change. Returns the rows of each SELECT and the number of rows each other statement changed. COMMIT and ROLLBACK statements are refused: the call controls the transaction. Serial increments (AUTO_INCREMENT, NEXT_VALUE) are not undone by a rollback.
Inputs: sql (required), database (required), host, port, +6 more.
explain_queryShow a CoraDB query planARead-only
The plan the CoraDB optimizer chooses for a SELECT (or for each SELECT of a script separated by ;), without running it.
Inputs: sql (required), database (required), host, port, +2 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 5 tools
Each tool targets a distinct activity: connection verification, schema introspection, read-only querying, mutating/DDL execution, and plan inspection. The only potential overlap is that execute_sql can also run SELECTs, but its description explicitly frames it as the transactional read-write path while execute_select is the guarded read-only path. Boundaries are clear enough that misselection is unlikely.
Four tools follow a clean verb_noun snake_case pattern (describe_schema, execute_select, execute_sql, explain_query). connection_info breaks the pattern as a noun phrase, a minor deviation but still unambiguous and readable.
Five tools is a tight, well-scoped surface for a database MCP: connect, inspect, read, write, explain. Nothing feels redundant or missing at the count level.
The lifecycle is well covered: reachability check, live catalog introspection, read queries, transactional writes/DDL with dry-run, and plan explanation. Minor gaps exist around multi-database enumeration/switching and any explicit result-pagination or cancellation path, but agents can work around these.
Related MCP Connectors
Read authorized Loam tables and supported external databases through a bounded MCP connection.
Governed data discovery, exact queries, decisions, simulations, and runtime utilities over MCP.
MCP Trust Firewall: tenant-approved tools, schema-pinned public reads. Authorization is required.
Query, join, profile, clean and convert CSV/JSON/Parquet with server-side DuckDB over MCP.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables SQL query execution and database structure browsing via MCP tools and resources.MIT
- AlicenseAqualityCmaintenanceEnables MCP clients to securely query databases, list tables, inspect table schemas, and test connections across Oracle, SQL Server, MySQL, PostgreSQL, and SQLite using read-only SQL tools.4MIT
- AlicenseNot gradedqualityCmaintenanceEnables MCP clients to execute SELECT-only SQL against a music-store schema with statement timeouts, guardrails, and retry/reflection support.MIT
- AlicenseNot gradedqualityCmaintenanceEnables database querying and modification through an MCP server where each action is gated by human-granted scopes, per-call confirmations for destructive operations, and revocable access.MIT