Skip to main content
This guide covers all configuration options for mcp-server-db2i. The server reads all of its settings from environment variables, set directly, in a .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 /auth flow with per-user DB credentials. Most secure.
  • token: Pre-shared static token. Uses environment DB credentials. Requires MCP_AUTH_TOKEN.
  • none: No authentication. Uses environment DB credentials. Only for trusted networks. The server refuses to start if MCP_HTTP_HOST is not loopback, unless MCP_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.
Disabled tools are not listed by 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 as rowCount: 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 returns BIGINT 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:
  1. Install a JDK (11 or later) and make sure JAVA_HOME points at it.
  2. Install or reinstall the server, for example npm install -g mcp-server-db2i, so node-jt400 builds.
  3. Set DB2I_DRIVER=jt400, or driver: jt400 on a profile.
If the package is missing, the first query fails with an error that names node-jt400. The server itself still starts.

Using the Mapepire driver (SSH)

The mapepire 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/java by default. Set javaPath to use another JDK.
Each Mapepire job is a JVM on the IBM i. Starting one takes a few seconds, and the first start takes longer because of the upload. The pool starts jobs when queries need them, up to 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 (or knownHostsFile). Plain and hashed entries are supported, and [host]:port entries off port 22.
If neither matches, the connection is refused and the error shows the key’s fingerprint. The simplest setup is to connect once with 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.
To remove the private install from the IBM i, delete $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 as IBM 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).
Check the registration with 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. Set DB2I_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 system argument 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 test is checked against the test profile’s allowedSchemas, or QUERY_ALLOWED_SCHEMAS when 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 /auth logs in to one system. Pass system in the request. The token is bound to that system, and its calls cannot switch to another. See HTTP transport.
The file is read once at startup and a mistake stops the server. Restart it after editing the file. When 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

The DB2I_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

The naming 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

The DB2I_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

The DB2I_SCHEMA variable sets a default schema for the metadata tools and for execute_query. When set:
  • You don’t need to specify schema in each metadata tool call
  • Tools will use this schema if no schema is provided
  • You can still override it per-call by providing a schema parameter
  • execute_query uses it as the library list: JDBC libraries unless DB2I_JDBC_OPTIONS already sets libraries, or ODBC DBQ unless DB2I_ODBC_OPTIONS already sets DBQ. With SQL naming, the first library is the default schema, so FROM CUSTOMERS resolves to MYLIB.CUSTOMERS. An explicit option always wins.
In HTTP 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.
Without a default schema, metadata tools require a 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.
When the list is set:
  • 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 unless OUTSIDELIB is 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 as UPPER and COALESCE are found. Clients cannot change the path, because SET statements are rejected. Keep libraries outside the list off the user profile’s library list.
  • Catalog libraries such as QSYS2 and SYSIBM are 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) and TABLE(...) table functions.
  • Names defined in a WITH clause are not treated as tables.
  • search_tables and search_columns search only the libraries in the list. Without a list, they skip system libraries (Q* and SYS*) unless include_system is true.
  • get_journal_info, index_advice, profile_table, list_routines, and describe_routine reject a library outside the list. They read QSYS2 catalog views themselves, so QSYS2 does not have to be in the list for them.
  • search_ibmi_services reads only the service catalog QSYS2.SERVICES_INFO, so the list does not apply to it. Running one of its examples with execute_query still needs the service’s schema in the list, and examples that call TABLE(...) table functions are rejected while the list is set.
  • list_tables, describe_table, list_views, list_indexes, and get_table_constraints reject a library outside the list before querying. list_schemas returns only the libraries in the list. Keys and indexes of an allowed table are still reported when they reference another library.
A view or alias inside an allowed library can still read other libraries. Give the IBM i user profile access only to the libraries in the list. See Security.

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:
  1. Environment variables (highest priority)
  2. .env file in the working directory
For npm scripts, the .env file is automatically loaded:
For Docker, use --env-file or the env_file directive in docker-compose.yml.