.env file, or in the env block of an MCP client. There are two ways to describe the database connection:
Start with the variables. Switch to profiles when you add a second system, or when each system needs its own driver, options or library allowlist. The single-system variables work the same way as a profile named
default.
When DB2I_PROFILES is set, it replaces the connection variables (DB2I_HOSTNAME, DB2I_USERNAME, DB2I_PASSWORD, DB2I_SCHEMA and the driver options). DB2I_DRIVER still applies, as the driver for profiles that don’t set their own. Everything else stays in environment variables either way: transport, HTTP auth, TLS, query limits, tool selection, rate limiting and logging. Profile passwords also come from the environment or from files, never from the YAML itself.
Quick Start
Create a.env file or set environment variables:
Environment Variables
Database Connection
*Either the environment variable or the corresponding
*_FILE variable must be set. File-based secrets take priority when both are provided.
Transport Settings
Over stdio, the server exits when its client goes away: when stdin closes, or when writing to stdout fails. It closes its connection pools first, the same as on
SIGINT, SIGTERM, or SIGHUP. Shutdown takes at most 5 seconds. A pool that is still waiting on a running statement after that is logged and left behind, and the process exits with code 1. In both mode, a closed stdin closes only the stdio connection pools, and the HTTP transport keeps serving.
HTTP Authentication Settings
Authentication Modes:
required(default): Full/authflow with per-user DB credentials. Most secure.token: Pre-shared static token. Uses environment DB credentials. RequiresMCP_AUTH_TOKEN.none: No authentication. Uses environment DB credentials. Only for trusted networks. The server refuses to start ifMCP_HTTP_HOSTis not loopback, unlessMCP_ALLOW_UNAUTHENTICATED_HTTP=true.
TLS Settings
Query Limits
Query exports
export_query writes query results to files on the server host. It is off until both of the first two variables are set.
Over stdio the result is the file path, so the user can open the file from the same machine. Over HTTP it is a download link, and the server only ever hands out links, never host paths. With
MCP_TRANSPORT=both, each caller gets the form for its transport.
The Mapepire driver gives an export a job of its own and never shares one, because cancelling at EXPORT_TIMEOUT ends whatever the job is running. When every job allowed by maxJobs is busy, the export is refused and can be tried again.
Tool Selection
Valid built-in names:
execute_query, export_query (also needs EXPORT_ENABLED), list_schemas, list_tables, search_tables, search_columns, describe_table, list_views, list_indexes, get_table_constraints, list_routines, describe_routine, validate_query, get_object_ddl, get_related_objects, get_journal_info, index_advice, profile_table, get_business_context, search_ibmi_services. Names are case-insensitive. An unknown name stops the server at startup, so a typo can’t silently leave a tool exposed.
When business SQL tools are loaded, the same variables also accept a custom tool name or toolset:<name>. A toolset selector matches only custom tools in that group. toolset:sales does not register execute_query.
tools/list and cannot be called. The setting applies to both stdio and HTTP transports.
Resources and prompts follow the tools they draw on. Disabling describe_table removes db2i://{schema}/{table} and all three prompts. Disabling get_object_ddl removes db2i://{schema}/{table}/ddl, and disabling get_business_context removes db2i://business-context. See Resources and prompts.
Business SQL tools
The server reads these files before it accepts connections. A statement that is not a query, or that names a library outside
QUERY_ALLOWED_SCHEMAS, stops startup. See Business SQL tools for the file format, parameter binding, and the example ERP pack.
Response Format
json(default): Compact JSON, no indentation.pretty: Indented JSON. Easier to read, but uses more tokens.markdown: Row results (data) become a markdown table with a summary line such asrowCount: 2, limitApplied: 1000. Results without rows fall back to compact JSON.
structuredContent always holds the raw result object, whatever this setting is. Only the text content changes.
Rate Limiting
The login limit guards against password guessing.
POST /auth and the OAuth sign-in form share it. The OAuth limit covers registration, sign-in, token and revocation requests. Its variables are only read with MCP_OAUTH_ENABLED. Neither limit has an off switch, and RATE_LIMIT_ENABLED does not affect them. Each value must be a positive whole number, and a window can be at most 2147483647 ms (about 24.8 days), or the HTTP server does not start.
Both limits count per client address. Raise them when many users arrive from one address: behind NAT or a proxy, or through a hosted client such as claude.ai, whose requests come from a shared outbound range (160.79.104.0/21).
Logging
Example Configuration
Minimal (stdio mode)
Full Configuration
Database Drivers
DB2I_DRIVER picks how the server talks to Db2 for i. All drivers run the same tools and apply the same defaults: system naming, ISO dates, a read-only query connection, DB2I_SCHEMA as the library list, and a second connection without the read-only setting for QSYS2.GENERATE_SQL (get_object_ddl). Each driver is loaded on first use, so the default odbc driver never starts Java.
Pick by what the network allows: odbc and jt400 need the database host server ports (8471 and friends) open to the MCP server. mapepire needs only SSH (port 22).
All driver packages are optional dependencies, so
npm install succeeds when one of them cannot build. If the odbc prebuilt binary is missing for your platform, npm install builds it from source and needs the unixODBC headers (unixodbc-dev on Debian and Ubuntu, unixODBC-devel on RHEL and SUSE).
Values that differ by driver
Every driver returnsBIGINT beyond the JavaScript safe integer range as an exact string, and binary columns (BINARY, VARBINARY, BLOB, CHAR FOR BIT DATA, VARCHAR FOR BIT DATA) as upper-case hex text, such as "0AFF".
The JDBC option translate binary=true changes that for jt400 and mapepire: FOR BIT DATA columns come back as text converted from EBCDIC. Bytes that have no character are lost (X'00FF' comes back as an empty string), so leave the option off and convert the columns that hold text in the query instead, for example CAST(<column> AS CHAR(10) CCSID 37).
The odbc driver reads every DECIMAL and NUMERIC value as a JavaScript number, so a value with more than 15 digits comes back rounded: DECIMAL(31,2) 12345678901234567890.12 arrives as 12345678901234567000. The odbc package has no setting to read these columns as text. When a result has such a value, execute_query and business SQL tools add a warnings entry that names the column. To keep every digit, select the column as CAST(<column> AS VARCHAR(40)), or use the jt400 or mapepire driver, which return wide decimals as exact text.
Using the JT400 driver
node-jt400 builds a native Java bridge during npm install. Without a JDK the build fails, npm skips the package, and the install still succeeds with ODBC only. To use JT400:
- Install a JDK (11 or later) and make sure
JAVA_HOMEpoints at it. - Install or reinstall the server, for example
npm install -g mcp-server-db2i, sonode-jt400builds. - Set
DB2I_DRIVER=jt400, ordriver: jt400on a profile.
node-jt400. The server itself still starts.
Using the Mapepire driver (SSH)
Themapepire driver reaches Db2 for i through Mapepire over SSH. It logs in with SSH as the configured user and starts the Mapepire server inside that session. No Mapepire daemon runs on the IBM i, no port besides SSH is used, and no administrator install is needed.
On the first connection, mapepire-js uploads its bundled server JAR (about 10 MB) to $HOME/.mapepire in the user’s home directory. Later connections reuse it. A JAR that Code for i already installed in $HOME/.vscode is reused too. To use an installed server instead, for example the mapepire-server RPM, set serverPath.
Requirements on the IBM i:
- The SSH daemon running, and the user allowed to log in with SSH.
- A home directory for the user, which must exist and be writable.
- Java 8 or later. mapepire-js uses
/QOpenSys/QIBM/ProdData/JavaVM/jdk80/64bit/bin/javaby default. SetjavaPathto use another JDK.
maxJobs, and closes each one after idleTimeout without queries. The SSH session closes with the last job, and the next query opens it again. Over HTTP with MCP_AUTH_MODE=required, every /auth login starts a job to check the credentials, so a login takes several seconds.
The JDBC connection runs on the IBM i with the JT400 driver, so DB2I_JDBC_OPTIONS applies as it does for jt400. That includes the read-only default (access=read only) and libraries. The session is encrypted by SSH, so secure=true is not needed.
Host key check
Before sending the password, the driver checks the IBM i’s SSH host key. The key must match either:- a fingerprint pinned with
hostKey=SHA256:..., or - an entry for the host in
~/.ssh/known_hosts(orknownHostsFile). Plain and hashed entries are supported, and[host]:portentries off port 22.
ssh user@host from the same machine and account, check the fingerprint, and accept it. To pin a key instead, get its fingerprint from the IBM i administrator (ssh-keygen -lf on the host key .pub file in the sshd configuration directory), and set hostKey to it. The connection error also shows the fingerprint the host presented, but check it through another channel before pinning it.
insecureHostKey=true turns the check off, and startup logs a warning. Use it only on a network you trust: a spoofed host would receive the password.
Options
DB2I_MAPEPIRE_OPTIONS takes semicolon-separated key=value pairs. Keys are case-insensitive, and an unknown key stops startup.
$HOME/.mapepire in the user’s home directory.
Installing the IBM i Access ODBC Driver
The driver is part of IBM i Access Client Solutions (ACS), but not of the ACS base download (the Java package with the 5250 emulator and Run SQL Scripts). It ships in the optional Linux, Mac, and PASE Application Package and Windows Application Package, and IBM also publishes the Linux and macOS packages from its own repositories (instructions). On every platform the driver registers asIBM i Access ODBC Driver, which is the name the server uses unless DB2I_ODBC_OPTIONS sets DRIVER or DSN.
Linux on arm64 (for example Raspberry Pi or Graviton) is not covered: IBM publishes no arm64 Linux build. Use the
jt400 driver there, or run the odbc image under amd64 emulation (see docker.md).
odbcinst -q -d on Linux, macOS and PASE, or in the ODBC Data Source Administrator on Windows. The odbc npm package ships prebuilt binaries for Linux x64, macOS and Windows x64; on other targets npm install builds it and needs the unixODBC headers.
Windows notes:
- IBM ships the Windows driver for x64 and x86 only. On Windows on ARM install the x64 build of Node.js; it runs under emulation and can load the x64 driver. A native ARM64 Node.js cannot load it.
Multiple Systems
One server can reach several IBM i systems, for example production and test, or two partitions. SetDB2I_PROFILES to a YAML file with one profile per system (example). For a single system, the environment variables are simpler, so you don’t need a profiles file.
*Set
username or usernameFile, and password or passwordFile. A mapepire profile with privateKeyFile in mapepireOptions needs no password. A path may itself be a "${ENV_VAR}" reference. Quote every reference: inside a { } map YAML reads a bare ${...} as another map.
How calls pick a system:
- The first profile is the default. A tool call that names no system runs there, and so do resources and prompts.
- Built-in tools take an optional
systemargument when more than one system is configured. Its values are the profile names. With one system the argument is not offered, so tool schemas are unchanged. - YAML tools can set
system:to always run on one system. See Business SQL tools. - Each system has its own allowlist and default library. A query on
testis checked against thetestprofile’sallowedSchemas, orQUERY_ALLOWED_SCHEMASwhen the profile sets none. - Pools are per system. A system’s pool opens on its first query and closes at shutdown. Startup checks only the default system’s connection.
- HTTP
/authlogs in to one system. Passsystemin the request. The token is bound to that system, and its calls cannot switch to another. See HTTP transport.
DB2I_PROFILES is set, DB2I_HOSTNAME and the other connection variables are ignored, and startup logs a warning if they are also set.
JDBC Options
TheDB2I_JDBC_OPTIONS variable accepts semicolon-separated JDBC options for the JT400/JTOpen driver. It applies when DB2I_DRIVER is jt400 (see Using the JT400 driver) or mapepire, whose server uses JT400 on the IBM i.
Common Options
The server sets
access=read only on every connection unless DB2I_JDBC_OPTIONS already contains access. That stops a statement the SQL validator misses from running as a write. Built-in tools only issue SELECT, so the default does not change them.
Set secure=true only after the IBM i host servers are configured for SSL (Digital Certificate Manager). Until then the user, password, and results cross the network in cleartext, and the server says so at startup.
Examples
Naming Conventions
Thenaming option affects how you reference tables:
sql(recommended): Use schema.table syntax (e.g.,MYLIB.CUSTOMERS)system: Use library/file syntax (e.g.,MYLIB/CUSTOMERS)
ODBC Options
TheDB2I_ODBC_OPTIONS variable accepts semicolon-separated connection keywords for the IBM i Access ODBC Driver. It applies when DB2I_DRIVER is odbc, the default. Keywords are case-insensitive and most have a long alias (NAM or Naming). IBM documents the full list under Connection string keywords.
Common Keywords
The server sets
CONNTYPE=2 on every query connection unless DB2I_ODBC_OPTIONS already contains CONNTYPE. The get_object_ddl connection omits CONNTYPE, so QSYS2.GENERATE_SQL can return its result set; that connection runs only the CALL.
Set SSL=1 only after the IBM i host servers are configured for TLS (Digital Certificate Manager). DB2I_PORT is not used: the ODBC driver connects to the host servers (8471, or 9471 with TLS), not the DRDA port.
A SYSTEM, UID or PWD value containing ;, = or { is wrapped in braces automatically. A value containing } cannot be expressed in an ODBC connection string and is rejected at startup.
Examples
Default Schema
TheDB2I_SCHEMA variable sets a default schema for the metadata tools and for execute_query. When set:
- You don’t need to specify
schemain each metadata tool call - Tools will use this schema if no schema is provided
- You can still override it per-call by providing a
schemaparameter execute_queryuses it as the library list: JDBClibrariesunlessDB2I_JDBC_OPTIONSalready setslibraries, or ODBCDBQunlessDB2I_ODBC_OPTIONSalready setsDBQ. With SQL naming, the first library is the default schema, soFROM CUSTOMERSresolves toMYLIB.CUSTOMERS. An explicit option always wins.
required mode, the schema sent to /auth is used for that session and falls back to DB2I_SCHEMA when the client omits it. It must be a single library name (up to 10 characters: letters, digits, _, $, #, @, not starting with a digit or _). Anything else is rejected with a 400.
schema argument, and an unqualified table name in execute_query resolves to the schema named after the user profile.
Schema Allowlist
QUERY_ALLOWED_SCHEMAS limits which libraries the built-in tools, business SQL tools, resources, and prompts may query or describe on IBM i. get_business_context and db2i://business-context only return YAML annotations, which the list does not filter. It is off when unset or empty. It is read from the server environment only, so a client cannot widen it by choosing a different schema at /auth. get_related_objects omits dependents whose schema is outside the list. A business SQL tool that names a library outside the list stops the server at startup. search_tables and search_columns only read libraries in the list, and reject a schema argument outside it.
QUERY_PARSE_CHECK controls the QSYS2.PARSE_STATEMENT check inside execute_query and inside business SQL tools. It is on unless set to false or 0. A statement that does not parse, or that is not a query, is rejected. If the function is not installed, the query is rejected until the check is turned off. Business tools cache the parse result after the first call.
The check is one extra round trip before the query. The added time is roughly fixed, often a few hundred milliseconds, and does not grow with the query. On a short query that can be most of the wait. Turn it off when that latency matters more than the extra syntax check. validate_query is separate: it also looks up names in the catalog, so it is slower than this check.
- Every table reference must be in the list. An unqualified name counts as the effective default schema (the session schema, or
DB2I_SCHEMA). - Every schema-qualified function call must be in the list too, so
OUTSIDELIB.F(ORDERNO)is rejected unlessOUTSIDELIBis listed. Unqualified functions are not checked: they resolve through the SQL path (the job’s library list under the default system naming), which is how built-ins such asUPPERandCOALESCEare found. Clients cannot change the path, becauseSETstatements are rejected. Keep libraries outside the list off the user profile’s library list. - Catalog libraries such as
QSYS2andSYSIBMare not included automatically. Add them if clients should query the catalog. - A query the server cannot parse is rejected. That includes system naming (
LIB/FILE) andTABLE(...)table functions. - Names defined in a
WITHclause are not treated as tables. search_tablesandsearch_columnssearch only the libraries in the list. Without a list, they skip system libraries (Q*andSYS*) unlessinclude_systemis true.get_journal_info,index_advice,profile_table,list_routines, anddescribe_routinereject a library outside the list. They readQSYS2catalog views themselves, soQSYS2does not have to be in the list for them.search_ibmi_servicesreads only the service catalogQSYS2.SERVICES_INFO, so the list does not apply to it. Running one of its examples withexecute_querystill needs the service’s schema in the list, and examples that callTABLE(...)table functions are rejected while the list is set.list_tables,describe_table,list_views,list_indexes, andget_table_constraintsreject a library outside the list before querying.list_schemasreturns only the libraries in the list. Keys and indexes of an allowed table are still reported when they reference another library.
File-Based Secrets
For secure credential management, use file-based secrets instead of environment variables:
File-based secrets take priority over plain environment variables. See the Security Guide for more details on credential management.
Loading Configuration
The server loads configuration from:- Environment variables (highest priority)
.envfile in the working directory
.env file is automatically loaded:
--env-file or the env_file directive in docker-compose.yml.