netsuite-suiteanalytics-odbc-mcp
# NetSuite SuiteAnalytics ODBC MCP
A local, read-only Model Context Protocol (MCP) server for querying a user's own NetSuite SuiteAnalytics Connect account through the user's locally installed ODBC driver.
> This project is an independent community project. It is not affiliated with,
> sponsored by, or endorsed by Oracle or NetSuite. NetSuite and SuiteAnalytics
> Connect are trademarks or products of Oracle.
Use this software only with accounts, data, credentials, roles, services, and driver software that you are authorized and licensed to use. Configure a dedicated least-privilege NetSuite role. This project does not grant a NetSuite subscription, SuiteAnalytics Connect entitlement, driver license, or right to access any data.
## What It Does
The server exposes exactly four MCP tools:
| Tool | Purpose |
| --- | --- |
| `netsuite_query` | Execute one bounded, read-only `SELECT` or `WITH` query. |
| `netsuite_tables` | Search bounded ODBC table metadata visible to the configured role. |
| `netsuite_describe` | Describe columns for one exact table visible to the configured role. |
| `netsuite_status` | Return secret-safe status and optionally perform a live connection check. |
The MCP client launches this package as a local child process and communicates over standard input/output. The Node.js process uses the system ODBC driver manager and the user's named local data source (DSN) to connect directly to NetSuite. There is no HTTP listener, remote transport, or hosted service.
The proprietary NetSuite ODBC driver and installer are **never bundled, uploaded, downloaded, mirrored, or licensed by this package**. Each user must obtain the driver independently from an authorized NetSuite account, accept the applicable terms, and install and configure it locally. Java, JDBC, and `NQjc.jar` are not required.
## Requirements
- Node.js 20 or later. `NETSUITE_DRIVER_HOME` self-replacement requires Node.js 22.15+ on POSIX systems.
- A supported local environment for the native Node `odbc` addon. Native binaries are platform-, architecture-, Node ABI-, and driver-manager-specific; installation may require platform build tools.
- A system ODBC driver manager: unixODBC on macOS/Linux, or the Windows ODBC Data Source Administrator on Windows.
- SuiteAnalytics Connect enabled and licensed for the user's NetSuite account.
- The latest ODBC driver available from the user's authorized account. Actual compatibility with the selected OS, architecture, Node runtime, driver manager, NetSuite release, and account must be tested locally.
- A local system or user DSN whose name exactly matches `NETSUITE_ODBC_DSN`.
- A dedicated least-privilege role with Connect access and only the record permissions needed for the intended use.
- A TBA integration and access token assigned to the intended user and that exact role.
- Outbound DNS and network access to the account-specific Connect service host on port `1708`.
ODBC access does not expose every NetSuite record, field, feature, saved search, workbook, SuiteScript API, REST record, or SuiteQL surface. The `NetSuite2.com` Connect schema and the configured role determine what is available.
## Quick Start
1. In an authorized NetSuite account, go to **Settings > Set Up SuiteAnalytics Connect**.
2. Choose the correct operating system, record the values under **Your Configuration**, accept the applicable terms, then download, extract, and install the ODBC driver locally.
3. Configure a system or user DSN using the installed driver and the recorded NetSuite configuration. Set `NETSUITE_ODBC_DSN` to that DSN's exact name.
4. Create a dedicated least-privilege role, TBA integration, and token as described in [Token-based authentication](docs/tba.md).
5. Run the exact public package version without a global installation:
```sh
npx -y netsuite-suiteanalytics-odbc-mcp@1.0.2 doctor
```
For development from a source checkout, use `npm install` followed by `npm run build`.
6. Supply the required environment through the MCP launcher's protected configuration. These are placeholders, not working values:
```text
NETSUITE_ACCOUNT_ID=YOUR_ACCOUNT_ID
NETSUITE_ROLE_ID=YOUR_NUMERIC_ROLE_ID
NETSUITE_SERVICE_HOST=YOUR_SERVICE_HOST_FROM_NETSUITE
NETSUITE_ODBC_DSN=YOUR_LOCALLY_CONFIGURED_NETSUITE_DSN
NETSUITE_CONSUMER_KEY=YOUR_CONSUMER_KEY
NETSUITE_CONSUMER_SECRET=YOUR_CONSUMER_SECRET
NETSUITE_TOKEN_ID=YOUR_TOKEN_ID
NETSUITE_TOKEN_SECRET=YOUR_TOKEN_SECRET
NETSUITE_DRIVER_HOME=/opt/netsuite/odbcclient
```
7. Run local diagnostics, then deliberately run the live test when authorized:
```sh
node dist/cli.js doctor
node dist/cli.js test-connection
```
`doctor` is local-only with respect to NetSuite: it validates configuration, loads the native ODBC addon/driver manager, and performs a DNS lookup, but does not open the DSN, authenticate, or query NetSuite. `test-connection` is live: it opens the configured DSN with TBA and executes `SELECT 1`, which may create NetSuite audit or connection records.
8. Configure the MCP client to launch the exact package version in stdio mode:
```json
{
"mcpServers": {
"netsuite": {
"command": "npx",
"args": ["-y", "netsuite-suiteanalytics-odbc-mcp@1.0.2", "serve"],
"env": {
"NETSUITE_ACCOUNT_ID": "YOUR_ACCOUNT_ID",
"NETSUITE_ROLE_ID": "YOUR_NUMERIC_ROLE_ID",
"NETSUITE_SERVICE_HOST": "YOUR_SERVICE_HOST_FROM_NETSUITE",
"NETSUITE_ODBC_DSN": "YOUR_LOCALLY_CONFIGURED_NETSUITE_DSN",
"NETSUITE_CONSUMER_KEY": "YOUR_CONSUMER_KEY",
"NETSUITE_CONSUMER_SECRET": "YOUR_CONSUMER_SECRET",
"NETSUITE_TOKEN_ID": "YOUR_TOKEN_ID",
"NETSUITE_TOKEN_SECRET": "YOUR_TOKEN_SECRET"
}
}
}
}
```
Client configuration formats vary. Protect the child-process environment. Do not pass credentials as CLI arguments; the CLI rejects configuration arguments. A tool caller cannot choose a DSN, connection string, account, role, or credentials. All four tools use the single operator-configured `NETSUITE_ODBC_DSN` and environment.
## LibreChat Setup
The package is published as [`netsuite-suiteanalytics-odbc-mcp`](https://www.npmjs.com/package/netsuite-suiteanalytics-odbc-mcp). LibreChat runs the pinned `1.0.2` release as a local `stdio` MCP process.
The LibreChat backend must run in the same environment that has Node.js 20+, the ODBC driver manager, the authorized NetSuite ODBC driver, and the configured DSN. The simplified `NETSUITE_DRIVER_HOME` setup shown below requires Node.js 22.15+ on Linux/macOS. For Docker deployments, those dependencies and the DSN must exist inside the LibreChat backend container; installing them only on the Docker host is not enough. Do not put the proprietary driver or credentials in a public image.
Set these values in LibreChat's protected server-side environment, not directly in source-controlled `librechat.yaml`:
```text
NETSUITE_ACCOUNT_ID=YOUR_ACCOUNT_ID
NETSUITE_ROLE_ID=YOUR_NUMERIC_ROLE_ID
NETSUITE_SERVICE_HOST=YOUR_SERVICE_HOST_FROM_NETSUITE
NETSUITE_ODBC_DSN=YOUR_LOCALLY_CONFIGURED_NETSUITE_DSN
NETSUITE_CONSUMER_KEY=YOUR_CONSUMER_KEY
NETSUITE_CONSUMER_SECRET=YOUR_CONSUMER_SECRET
NETSUITE_TOKEN_ID=YOUR_TOKEN_ID
NETSUITE_TOKEN_SECRET=YOUR_TOKEN_SECRET
```
Add this entry to `librechat.yaml`:
```yaml
mcpServers:
netsuite-suiteanalytics:
title: NetSuite SuiteAnalytics
description: Bounded read-only access to the configured SuiteAnalytics Connect role
type: stdio
command: npx
args:
- -y
- netsuite-suiteanalytics-odbc-mcp@1.0.2
- serve
env:
NETSUITE_ACCOUNT_ID: '${NETSUITE_ACCOUNT_ID}'
NETSUITE_ROLE_ID: '${NETSUITE_ROLE_ID}'
NETSUITE_SERVICE_HOST: '${NETSUITE_SERVICE_HOST}'
NETSUITE_ODBC_DSN: '${NETSUITE_ODBC_DSN}'
NETSUITE_CONSUMER_KEY: '${NETSUITE_CONSUMER_KEY}'
NETSUITE_CONSUMER_SECRET: '${NETSUITE_CONSUMER_SECRET}'
NETSUITE_TOKEN_ID: '${NETSUITE_TOKEN_ID}'
NETSUITE_TOKEN_SECRET: '${NETSUITE_TOKEN_SECRET}'
NETSUITE_DRIVER_HOME: /opt/netsuite/odbcclient
ODBCINI: /etc/odbc.ini
NETSUITE_SERVICE_PORT: '1708'
NETSUITE_DATA_SOURCE: NetSuite2.com
NETSUITE_TELEMETRY: 'off'
initTimeout: 30000
timeout: 130000
stderr: inherit
serverInstructions: true
chatMenu: false
```
Before starting LibreChat, validate the setup from the same host or container and operating-system user that runs the LibreChat backend:
```sh
npx -y netsuite-suiteanalytics-odbc-mcp@1.0.2 doctor
npx -y netsuite-suiteanalytics-odbc-mcp@1.0.2 test-connection
```
`test-connection` makes a live TBA connection and executes `SELECT 1`. Restart LibreChat after changing `librechat.yaml`. In the Agent Builder, select **Add MCP Server Tools**, choose **NetSuite SuiteAnalytics**, and enable only the required tools. `chatMenu: false` keeps the tools agent-only; enable it only after reviewing access controls and data exposure.
This local MVP uses one process-level NetSuite credential set and does not provide per-user OAuth or tenant isolation. Do not expose one shared NetSuite account to unrelated LibreChat users. Query results can enter LibreChat history and model-provider requests, so review user access, retention, tracing, backups, sharing, and model-provider approval before enabling the server.
### LibreChat Container Checklist
LibreChat stdio children receive only the environment explicitly configured for that MCP server. Variables present in an interactive shell or the parent container are not enough. Include every `NETSUITE_*` value above and `NETSUITE_DRIVER_HOME` in the MCP `env` block.
`NETSUITE_DRIVER_HOME` is the absolute directory containing the extracted `odbcclient`. It must not be writable by group or other users. The CLI replaces itself in place where supported and derives:
```text
OASDK_ODBC_HOME=/opt/netsuite/odbcclient/lib64
LD_LIBRARY_PATH=/opt/netsuite/odbcclient/lib64:/opt/netsuite/odbcclient/lib:/opt/netsuite/odbcclient:...
```
Explicit `OASDK_ODBC_HOME`, `ODBCINI`, and loader-path values take precedence if the installed driver requires different paths. The self-replacement is necessary because Linux resolves `LD_LIBRARY_PATH` when the process starts.
When `lib64/ivoa27.ini` exists, `OASDK_ODBC_HOME` is derived as `/opt/netsuite/odbcclient/lib64`; otherwise it falls back to the extraction root. On Node.js 20, omit `NETSUITE_DRIVER_HOME` and place `OASDK_ODBC_HOME=/opt/netsuite/odbcclient/lib64`, `ODBCINI`, and `LD_LIBRARY_PATH` explicitly in LibreChat's MCP `env` block. Those values are then present when the process starts, so no self-replacement is needed.
For a container deployment, make all setup persistent rather than repairing a running container:
1. Extract the authorized driver on the host into a git-ignored private directory such as `./netsuite-odbc/odbcclient`. Do not commit or publish the zip or extracted driver.
2. Bind-mount the extracted directory read-only at `/opt/netsuite/odbcclient`.
3. Bind-mount reviewed `odbc.ini` and `odbcinst.ini` files read-only at `/etc/odbc.ini` and `/etc/odbcinst.ini`.
4. Build a private LibreChat API image containing the required ODBC/runtime packages so container recreation does not erase them.
5. Pin and optionally preinstall `netsuite-suiteanalytics-odbc-mcp` in that private image to avoid relying on a warm `npx` cache.
Copy the maintained templates from [`examples/librechat`](examples/librechat):
```text
examples/librechat/Dockerfile.odbc
examples/librechat/docker-compose.override.example.yml
```
Place `Dockerfile.odbc` alongside LibreChat's Compose project, copy the override as `docker-compose.override.yml`, and set `LIBRECHAT_API_IMAGE` to the exact pinned upstream API image. Review the upstream runtime user before building.
The NetSuite Linux driver is glibc-linked. LibreChat Alpine images use musl, so a typical private image needs:
```dockerfile
# Extend the exact LibreChat API image version used by your deployment.
FROM YOUR_LIBRECHAT_API_IMAGE
USER root
RUN apk add --no-cache \
gcompat libc6-compat linux-pam libstdc++ \
unixodbc unixodbc-dev
RUN npm install --global netsuite-suiteanalytics-odbc-mcp@1.0.2
USER node
```
Confirm `node` is the non-root user in the exact upstream image; restore its actual user if different. If the native npm `odbc` addon must compile instead of using a compatible prebuilt binary, the private image may also need `python3`, `make`, and `g++`. Never add the proprietary NetSuite driver to a public image.
When the package is preinstalled globally in the private image, avoid `npx` cache/network resolution by changing the LibreChat entry to:
```yaml
command: netsuite-mcp
args:
- serve
```
A compose override can provide only the private runtime files:
```yaml
services:
api:
volumes:
- ./netsuite-odbc/odbcclient:/opt/netsuite/odbcclient:ro
- ./netsuite-odbc/odbc.ini:/etc/odbc.ini:ro
- ./netsuite-odbc/odbcinst.ini:/etc/odbcinst.ini:ro
```
The DSN name must exactly match `NETSUITE_ODBC_DSN`. Its host, port `1708`, `NetSuite2.com` source, driver library, and truststore must point to files visible inside the container. Any `RoleID` in `odbc.ini` or the driver's `odbc64.ini` must match `NETSUITE_ROLE_ID`; the access token must also belong to that exact role.
### Self-Service Diagnosis
Run both commands inside the LibreChat API container, as the same user that launches the MCP child:
```sh
npx -y netsuite-suiteanalytics-odbc-mcp@1.0.2 doctor
npx -y netsuite-suiteanalytics-odbc-mcp@1.0.2 test-connection
```
`doctor` stays local-only. It checks the native addon/driver manager, unixODBC DSN registration, designated driver home, `odbc64.ini` role alignment, designated `ODBCINI`, registered driver library, declared truststore, DNS, and any host/port/data-source declarations present in the DSN. It does not authenticate. Omitted or vendor-specific DSN keys cannot always be inferred, so the live check remains required.
`test-connection` is explicit and live. It loads the registered NetSuite driver, generates a fresh TBA password, opens a connection, and executes `SELECT 1`. Safe error categories distinguish missing DSNs, driver initialization/runtime failures, authentication failures, network/port failures, TLS failures, and query/session failures without returning raw driver diagnostics.
Common recovery order:
1. `DRIVER_NOT_FOUND`: verify the DSN is registered for the runtime user and its name exactly matches `NETSUITE_ODBC_DSN`.
2. `DRIVER_LOAD_FAILED`: verify the mounted driver home, `ivoa27.so` registration, glibc/PAM/C++ runtime packages, library path, and ODBC configuration files.
3. `INVALID_TOKEN` or `INVALID_ROLE`: align every local `RoleID`, `NETSUITE_ROLE_ID`, and the integration + user + role token assignment. Stop repeated attempts before lockout.
4. `DNS_FAILURE` or `PORT_BLOCKED`: verify DNS and outbound TCP from the container to the exact account host on port `1708`.
5. `TLS_FAILURE`: verify the DSN truststore/certificate path is mounted and readable.
After changing LibreChat MCP configuration, use the Admin UI save path or restart LibreChat. Direct database edits can remain hidden behind application and failure caches. Renaming an MCP server can leave saved agent tool bindings pointing at the old server key; remove and re-add the MCP tools in Agent Builder.
Container recreation removes ad hoc `apk` packages, `/etc/odbc*.ini`, and `npx` cache contents. A custom private image plus read-only mounts avoids that fragility. A plain restart normally preserves the existing container filesystem. Live status and connection tests create real NetSuite connection/audit activity.
The package deliberately does not write `odbc.ini`, `odbcinst.ini`, alter the proprietary driver package, or install OS libraries: layouts and permitted installation procedures vary by driver release and platform, and those are privileged deployment operations. Use the current authorized NetSuite driver instructions, keep configuration in private deployment files, and use `doctor` to validate the result.
See the [full LibreChat guide](docs/librechat.md) and [setup guide](docs/setup.md) for deployment details, limits, and security boundaries.
## Safety Model
- Only one `SELECT` statement or read-only `WITH` query is accepted.
- SQL comments, semicolons, write/DDL/control keywords, and unsupported or ambiguous syntax are rejected locally.
- Row count, serialized row bytes, timeout, pool size, concurrency, and physical connection lifetime are bounded. Native driver buffers may allocate beyond the serialized response cap.
- Optional table allowlists and denylists narrow local access.
- The configured NetSuite role is the authoritative access boundary. Local SQL checks are defense in depth, not a substitute for least privilege.
- A fresh Connect token password is generated only when a new physical ODBC connection is created. Pooled operations reuse that connection until it expires or closes.
- Three consecutive classified authentication failures open a local circuit breaker for a configurable cooldown.
- Project telemetry is fixed to `off`. NetSuite, the ODBC driver, the MCP client, the selected model provider, the operating system, and dependencies may have their own logging or telemetry.
- Errors are categorized and redacted before being returned, but logs, queries, results, and MCP transcripts must still be treated as potentially sensitive.
## CLI
```sh
npx -y netsuite-suiteanalytics-odbc-mcp@1.0.2 serve
npx -y netsuite-suiteanalytics-odbc-mcp@1.0.2 doctor
npx -y netsuite-suiteanalytics-odbc-mcp@1.0.2 test-connection
npx -y netsuite-suiteanalytics-odbc-mcp@1.0.2 print-example-config
```
`serve` is the default. Pin an exact version rather than relying on the npm `latest` tag. With a source build, substitute `node dist/cli.js` for the `npx` command. Standard output carries MCP protocol messages while serving; do not wrap the process with software that writes banners or logs to stdout.
## Scope
This release supports one local, single-user stdio process. It does not provide HTTP or another remote transport, a hosted service, multi-user tenancy, writes, DDL, SuiteScript, REST access, credential hosting, driver acquisition, driver redistribution, automatic NetSuite role provisioning, or arbitrary SQL compatibility.
Kiokii-originated queries may require changes for the SuiteAnalytics Connect ODBC SQL dialect, `NetSuite2.com` schema, identifier quoting, functions, joins, date handling, and pagination. Use `netsuite_tables` and `netsuite_describe` against the target account rather than assuming another connection method's schema or SQL is compatible. No Kiokii-specific schema, customization, adapter, or support guarantee is included.
## Documentation
- [Installation and configuration](docs/setup.md)
- [LibreChat integration](docs/librechat.md)
- [Maintainer publishing guide](docs/publishing.md)
- [Token-based authentication](docs/tba.md)
- [Troubleshooting](docs/troubleshooting.md)
- [Security, privacy, and responsibilities](docs/security.md)
- [Licensing and third-party software](docs/licensing.md)
- [Vulnerability reporting](SECURITY.md)
## License
The original project code and documentation are licensed under Apache License 2.0. The NetSuite service, ODBC driver, and installer are separate proprietary products governed by their owners' terms. See [LICENSE](LICENSE), [NOTICE](NOTICE), and [licensing guidance](docs/licensing.md). This repository does not provide legal advice.
TDQS
Scored across 4 tools
Each tool serves a clearly distinct purpose: running queries, listing metadata, describing table columns, and checking status. There is no overlap or ambiguity between them.
All tools share a consistent 'netsuite_' prefix, but the second part mixes verbs (query, describe) and nouns (tables, status). The pattern is predictable though not strictly verb_noun.
With only 4 tools, the set is tightly scoped to the server's purpose of running SuiteAnalytics ODBC queries and exploring metadata. Each tool is essential and the count feels appropriate.
The toolset covers the core lifecycle of working with an ODBC data source: querying data, discovering tables, inspecting schemas, and checking connection health. No obvious gaps exist for the intended use case.