AI & Automation Trends

Claude + MCP + NetSuite: A Working Setup in an Afternoon, and Six Reasons It Won't Survive Close

Wire Claude into live NetSuite with MCP — SuiteQL, RESTlets, OAuth — in an afternoon. Then the six reasons a hand-rolled server won't survive close.

Kai Jenson, Advisor, NSGPT · July 24, 2026

The short version: It takes about an afternoon to give Claude read access to your live NetSuite data over MCP — a token, a RESTlet or two, a SuiteQL query, and roughly 200 lines of server code. It takes about six weeks of real close work to find the six places that setup quietly falls short.

Both halves of that are true, and finance teams keep discovering them in the wrong order. The demo works. The Monday-morning production run is where the token expires mid-query, the agent trips a concurrency limit, and an auditor asks who ran what.

This is a practitioner's walkthrough of both halves — honestly. First, how to actually wire Claude to NetSuite with MCP and what genuinely works. Then the six limits you'll hit, why they're structural and not a bug in your code, and only then where a managed layer earns its place. No tool gets oversold. Everything ties back to NetSuite.

What MCP actually is

The Model Context Protocol is an open standard for letting an AI model call external tools and read external data through a small server you run. You write the server; it exposes a handful of tools (functions with typed arguments) and resources (readable context). Claude — in the desktop app, an IDE, or via the API — connects to the server, sees the tools, and decides when to call them. The transport is either stdio (the server runs as a local subprocess) or HTTP for something remote.

For NetSuite, the mental model is simple: the MCP server is a thin translator. Claude speaks "tools"; NetSuite speaks REST, RESTlets, and SuiteQL. The server's whole job is to turn a tool call like suiteql({ q: "SELECT ..." }) into an authenticated NetSuite request and hand the rows back.

The three doors into NetSuite

You have three ways in, and a good server usually uses two of them.

  • SuiteTalk REST — SuiteQL. The workhorse for reads. You POST a SQL-like query to the /query/v1/suiteql endpoint and get rows back, a page at a time (a thousand rows per page, then you paginate). One SuiteQL tool answers the majority of "what happened" questions — aging, balances, variance inputs — without deploying anything into NetSuite.
  • SuiteTalk REST — records. The /record/v1/ endpoints do straightforward CRUD on standard and custom records. Good for reading or (carefully) writing a specific record; weak for anything analytical.
  • RESTlets. Custom SuiteScript endpoints you deploy into the account. Reach for these when a query can't express the job — bespoke logic, a multi-step operation, or when you want to control exactly how much governance a call consumes.

If you're building for finance, start with a single SuiteQL tool. It's read-only by nature, it's expressive, and it keeps the agent out of write paths you don't want it near yet.

Auth: the part that eats the afternoon

Connecting is easy. Authenticating is where the time goes.

NetSuite gives you two real options, and they trade complexity differently:

  • Token-Based Authentication (TBA) — OAuth 1.0a. You create an Integration record (consumer key/secret), issue a token (token id/secret) tied to a user and a role, and sign every request with HMAC-SHA256 over a base string that includes your account id as the realm. The tokens are long-lived, which is exactly what a service wants — but the signature construction is fiddly, and a wrong nonce or timestamp fails with an opaque error.
  • OAuth 2.0 — cleaner to reason about, but the access token expires in about 60 minutes, so you own a refresh flow. Machine-to-machine (client credentials) uses a signed JWT with a certificate you manage.

Either way you'll also create a dedicated role and grant it the least access the job needs — for a finance read agent, that's log-in-via-access-token permission plus read on the records and the SuiteAnalytics/SuiteQL surface. Scope it tight. This role is your security boundary.

The server, in about 200 lines

Once auth works, the server is small. Here's the shape of a minimal read-only server exposing one SuiteQL tool over stdio:

// A minimal MCP server exposing one NetSuite tool: suiteql.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({ name: "netsuite", version: "0.1.0" });

server.tool(
  "suiteql",
  "Run a read-only SuiteQL query against live NetSuite.",
  { q: z.string().describe("A SuiteQL SELECT statement") },
  async ({ q }) => {
    const res = await fetch(
      `https://${ACCOUNT_ID}.suitetalk.api.netsuite.com` +
        `/services/rest/query/v1/suiteql?limit=1000`,
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Prefer: "transient",
          // TBA: OAuth 1.0a signature over the base string, HMAC-SHA256.
          Authorization: signTba("POST", url, { consumer, token }),
        },
        body: JSON.stringify({ q }),
      }
    );
    const { items, hasMore } = await res.json();
    return {
      content: [{ type: "text", text: JSON.stringify({ items, hasMore }) }],
    };
  }
);

await server.connect(new StdioServerTransport());

Point Claude at it, and you can ask: "What's our AR aging by subsidiary as of month-end?" Claude writes the SuiteQL, calls the tool, reads the rows, and hands you the answer with the query it used. That's the moment the demo feels like magic — and it's genuinely useful.

What genuinely works, today, with exactly this:

  • Read-only Q&A over live data. Ad-hoc questions you'd otherwise file as a SuiteQL ticket.
  • Exploration. "What custom fields are on the vendor bill record?" — the agent pokes around the schema faster than you can click.
  • Drafting. It writes SuiteQL and saved-search logic you refine, instead of starting from a blank query.
  • One-off pulls. The kind of question that comes up once and never justifies a dashboard.

Explore the whole path below — click any hop to see what holds up and where it cracks. Then flip the toggle.

Claude

Plans the query, calls the tools, reads the rows back, and writes the answer in plain English.

What works
  • Decomposes a vague ask into concrete steps
  • Writes SuiteQL and interprets the result set
  • Explains the "why" behind a number, not just the number
Where it falls short
  • A real GL is millions of lines — it will never fit the context window
  • It reasons only over the slice you page in; feed it the wrong slice and the answer is confidently wrong
Click any hop. Each "falls short" is one of the six limits in the piece. Illustrative of a typical hand-rolled setup — not a benchmark.

Six reasons it won't survive close

Here's the honest half. Everything above is real and works. It also stops being enough the moment you try to run it as infrastructure a finance team depends on. None of these are bugs in your code — they're structural, and they're the same six every hand-rolled server hits.

1. Auth is a moving target, not a setup step

You didn't finish auth; you started it. OAuth 2.0 access tokens expire in about an hour, so a long session needs silent refresh or it dies mid-analysis. TBA tokens don't expire but live as secrets in a config file, and the OAuth 1.0a signature is unforgiving — a clock skew or a stray character fails with an error that tells you nothing. And because there's one integration token, every question runs as the same service identity. There's no "Claude, acting as the AP clerk." There's just the token.

2. NetSuite governs you back

NetSuite meters API usage, and an agent is a heavy user by design. Two ceilings bite. Concurrency: your account allows only so many simultaneous requests across REST, RESTlets, and SOAP; exceed it and you get an HTTP 429. An agent that fans a question out into six parallel queries hits this fast. Governance units: server-side calls spend from a per-call budget (a RESTlet's is in the low thousands of "units"), and a complex pull can exhaust it and throw SSS_USAGE_LIMIT_EXCEEDED. Handling this properly means queuing, backoff, and staying under the ceiling — none of which a 200-line server does.

3. The schema is a swamp

SuiteQL assumes you know the schema. Real NetSuite accounts are years of custom fields, custom records, custom segments, and undocumented conventions — the custbody_ and custrecord_ sprawl no demo has. Ask a naive agent for "revenue by product line" and it will confidently query the wrong field, because it guessed. Generic text-to-SQL accuracy that looks like 85–90% on a clean schema falls off sharply on a production ERP. The model needs a map of your account's real structure; without one, it's fluent and wrong.

4. Real ERP data doesn't fit the context window

Your general ledger is millions of lines. Claude's context window — however large — is not millions of lines, and even if it were, paging the whole GL through it every question is absurd. So the agent reasons over the slice you page in. Feed it the wrong slice and you get a confident answer to a question you didn't ask. Doing this well is a retrieval problem — summarize, aggregate in SuiteQL, and hand the model the right thousand rows, not all of them. The naive server just paginates until something breaks.

5. There's no audit trail, and no permissions story

This is the one that ends the conversation with a CFO. A hand-rolled server has no record of who asked what, on whose behalf, under which role. Every query runs as one service token, so segregation of duties — the backbone of financial controls — is gone. When an auditor asks "show me every query this agent ran against the GL last quarter, and who authorized it," the honest answer is that nobody logged it. You can't put an unauditable black box between a person and the ledger and expect it to survive a SOX walkthrough.

6. Hand-rolled servers are brittle

The setup that dazzled in the demo runs on one developer's laptop. There's no monitoring, no retry, no backoff, no on-call. Secrets sit in a dotfile. Then NetSuite ships a release, a field changes, and the server silently returns wrong or empty results with no alert. The first 429 at month-end has no handler because month-end wasn't in the demo. Brittleness isn't a code-quality problem you can polish away — it's the difference between a script and a system.

The line: DIY vs. managed

Put the six together and a pattern falls out. The connection is a solved, weekend-sized problem — genuinely. What's hard is everything that makes the connection trustworthy at close: rotating auth and per-user identity, staying under governance limits, mapping a real schema, retrieving the right slice, logging every question for audit, and running as monitored infrastructure instead of a laptop script.

That's not a longer to-do list. It's a different kind of work — the unglamorous machinery, not the magic moment. Flip the toggle in the explorer above and you can see it hop by hop: the same path, reframed from where-it-breaks to what-closes-the-gap.

This is exactly the gap NSGPT productizes — the managed layer between Claude and NetSuite, with the auth rotation, rate governance, schema grounding, retrieval, and full audit trail already built, so a finance lead gets the afternoon's magic without inheriting the six-week bill. If you've built the DIY version and hit the wall, that's the conversation to have.

The bottom line

Build the DIY version. Seriously — it's the fastest way to understand what MCP and NetSuite can do together, and read-only Q&A over live data is worth having on its own. Just build it knowing what it is: a superb prototype and a genuinely useful exploration tool, not the audited, governed, monitored path you'd put between an agent and your ledger for a control or a close.

The connection is the easy 20%. The trust is the other 80% — and the other 80% is the whole job.

If you want to see that managed layer running on live NetSuite data — token rotation, rate governance, schema grounding, and every query auditable — request a walkthrough. Bring the question your DIY server choked on; that's where the line is easiest to see.

Kai Jenson

Advisor, NSGPT

Kai Jenson advises NetSuite finance teams on AI agents, forecasting, and analytics — writing from real NSGPT customer builds.

← All articles

See What Finance Teams Build with NSGPT

NetSuite-integrated AI agents, forecasting, and analytics — built and operated by your finance leads on live data.