Suite Utils

Why a plugin, not a skill

How the plugin differs from Oracle's netsuite-ai-connector-instructions skill, and which of its checks run as code rather than as instructions.

On this page

Oracle publishes a skill for the NetSuite AI Connector, netsuite-ai-connector-instructions, and documents it in the NetSuite help under Using Skills with Claude, ChatGPT, and Other MCP Clients. It covers which tool to call first, how to format answers, how to handle subsidiaries and currencies, and a SuiteQL safety checklist. The NetSuite team at Oracle wrote it and published it under the Universal Permissive License 1.0. This plugin's netsuite skill is derived from it, and the NOTICE file in the repository lists what we changed.

A skill is text that Claude reads. Claude decides whether to follow it, call by call. Nothing checks the query it writes before the query goes to NetSuite, and nothing changes what NetSuite sends back. Most of the time Claude follows the instructions. When it doesn't, the first sign is a number that looks plausible and is wrong.

This plugin keeps the skill and adds code that Claude Code runs on every connector call.

Where the checks run

A plugin can register hooks. A hook is a command Claude Code starts as its own process at a fixed point in a tool call, and Claude Code acts on what it returns. The model can't skip a hook, and it can't talk one into a different answer. This plugin registers four, all running node scripts/nsx.mjs hook …:

HookWhen it runsWhat it can do
SessionStartA session starts, resumes, clears or compactsAdd the account profile and a list of stale cache sections to the context
PreToolUseBefore any ns_* call leaves Claude CodeDeny the call with a reason, change its input, or send it to your approval prompt
PostToolUseAfter an ns_* call returnsCache catalog results, replace a large result with a summary and a file, write the audit log
PostToolUseFailureAfter an ns_* call failsClassify the error and tell Claude the next step

The three call hooks match mcp__.*__ns_.*, so they fire whatever name you gave the connector.

What happens to a bad query

Take a query that caps rows with ROWNUM next to GROUP BY. With only a skill, the checklist tells Claude not to write it. If Claude writes it anyway, NetSuite runs it. ROWNUM applies before the grouping, so the totals come from an arbitrary slice of rows, and nothing reports an error.

With the plugin, the PreToolUse hook parses the query before the call goes out. The rownum-with-aggregate rule matches, and the hook denies the call. The denial names the rule, says why the totals would be wrong, and tells Claude to end the query with ORDER BY … FETCH FIRST. Claude has to send a corrected query. The broken one never reaches NetSuite.

The checker is code, so the same query gets the same verdict every time. The one input that changes between runs is the cached metadata the column checks read. The full rule list, with which rules deny and which only warn, is on the SuiteQL checker page.

What else runs as code

  • Default caps. A SuiteQL call without a page size gets one, with pageIndex: 0, so it returns one page. A saved search without range_end gets a row cap. Both are added to the call's input, not suggested to Claude.
  • Large results. A result over inline_max_chars (6,000 characters by default) is saved to a local file. Claude gets the row count, column stats, the first rows and a result id instead of the raw rows. It then works on the file with nsx results.
  • Tables the role can't see. A query on a table missing from the cached record-type list is denied before the call, instead of failing at NetSuite.
  • Writes. Creates and updates are denied while read_only is on, which is the default. With writes on, each one needs a preview and a call with the same input, and then goes to Claude Code's permission prompt with the changes listed. A create without an externalId is denied, so a retried call can't make a duplicate record.
  • An audit log. Every NetSuite call is logged, including denied calls and writes waiting for approval.

If a hook fails, the call goes through unchanged. Writes are the exception: a write the plugin couldn't check is denied.

What stays with Claude

The code narrows what can go wrong. It doesn't do the thinking. Claude still picks the tables, writes the query and reads the result. The checker catches the patterns on its rule list. A query can pass every rule and still ask the wrong question. Warnings are advice, and [nolint] overrides the rules that can be overridden. That's why the plugin also ships the skill: the guidance for the parts code can't check is still worth having.

Where only the skill applies

Hooks run in Claude Code: the CLI, the Code tab in the Claude desktop app, or an IDE extension. Cowork and claude.ai install the plugin but don't run plugin hooks yet. There you get the skill's guidance and nothing else, which includes no write protection. Control writes there through the connector role's permissions.

Using both at once

Don't. Both skills load for the same questions and give Claude conflicting instructions, including the ROWNUM template this plugin blocks. If you installed Oracle's skill, remove it before you install the plugin.

Install su-ns-harness

Run both inside Claude Code, then /reload-plugins. Version 0.5.1, free and open source.

  1. /plugin marketplace add suiteutils/suiteutils-netsuite-claude-harness
  2. /plugin install su-ns-harness@suiteutils

Page last updated 2026-09-28. NetSuite is a trademark of Oracle. Claude is a trademark of Anthropic. Suite Utils is not affiliated with either.