AI Use Cases & Business Automation Examples | RAAS Impact

Building an AI Agent for NetSuite: From Working Auth to Working Agent (Part 2)

Written by Lino Moretto | Mar 18, 2026 6:47:01 PM

The complete implementation guide: building the ADK agent, handling tool calls, and what happens when an LLM meets real ERP complexity.

Where We Left Off

In Part 1 of this series, we tackled the hardest part of enterprise AI integrations: authentication.

We went through the research journey that led to mcp-remote as the solution to OAuth complexity, debugged NetSuite's non-standard token format (returning expires_in as a string instead of a number), patched the MCP SDK, and established a clean architecture that isolates all authentication complexity away from the agent code.

By the end of Part 1, we had a working authentication layer. But we didn't have an actual AI agent yet. We had the foundation, but not the house.

In this article, we build the complete agent on top of that foundation — and discover what happens when an LLM tries to talk to a real ERP system.

Building the Agent

The full source code is available on GitHub. Let's walk through the key pieces.

Project Structure

I used uv for project structure and dependency management — fast, clean, and it handles Python virtual environments well:

adk-netsuite-demo/
├── .env # Environment variables
├── .node/
│ └── mcp-remote/ # Patched mcp-remote package
├── main.py # Entry point
├── netsuite_agent/
│ ├── agent.py # Agent definition
│ └── tools/
│ └── netsuite.py # MCP toolset configuration
├── pyproject.toml # Dependencies
└── uv.lock

Environment Setup

We use gemini-2.5-flash for speed and cost-effectiveness, with a standard Google API key from Google AI Studio. The .env file holds the Google API key alongside the NetSuite credentials (Account ID, Client ID, Client Secret) configured via the NetSuite Integration setup.

The MCP Toolset

This is where we configure the connection to NetSuite using our patched mcp-remote from Part 1:

# netsuite_agent/tools/netsuite.py
mcp_tool = McpToolset(
connection_params=StdioConnectionParams(
server_params=StdioServerParameters(
command="npx",
args=[
"-y",
".node/mcp-remote",
# Local patched version
f"https://{os.environ['NETSUITE_ACCOUNT_ID']}.suitetalk.api.netsuite.com/services/mcp/v1/all",
"--static-oauth-client-info",
json.dumps({
"client_id": os.environ['NETSUITE_CLIENT_ID'],
"client_secret": os.environ['NETSUITE_CLIENT_SECRET'],
}),
"--static-oauth-client-metadata",
json.dumps({"scope": "mcp"}),
],
),
timeout=120,
),
)

The connection runs mcp-remote as a child process, passes NetSuite's MCP endpoint URL with OAuth credentials, and exposes everything via stdio so ADK treats it like a local tool.

The Agent Definition

I kept the agent deliberately minimal — the whole point of this POC was to validate whether Gemini can effectively use the tools exposed by NetSuite's MCP Server:

# netsuite_agent/agent.py
root_agent = Agent(
model='gemini-2.5-flash',
name='root_agent',
description="A helpful assistant for questions about business information in NetSuite.",
instruction="""
You are a helpful agent who can retrieve information from the user's NetSuite account.
Use the provided tools to answer the user's questions.
When querying NetSuite:
- Be specific about what data you're requesting
- Format responses in a clear, readable way
- If you're unsure about available data, ask the user for clarification
""",
tools=[netsuite.mcp_tool],
)

With this in place, you can fire up the agent with ADK's built-in tools — adk web for a browser interface or adk run netsuite_agent from the terminal.

The first time you run it, a browser window pops up asking you to authenticate with NetSuite. Once authorised, all session and refresh tokens are stored in your ~/.mcp-auth/ directory.

The Moment of Truth

Let's start simple: "Hi! What's the total turnover for 2024? My fiscal year starts on January 1st."

After the OAuth handshake completes, the agent connects, tool calls start flowing, and…

It works. The response was correct. The LLM successfully used NetSuite's MCP tools to query transaction data and return the right answer.

This is where the dream of "talk to your ERP in natural language" becomes very real. But it's also where things get interesting — because the agent isn't always this smooth.

The LLM Is Smart. The Tools… Need Work.

Using adk run is useful but doesn't show the full picture. Asking for a simple turnover figure required several tool calls behind the scenes. Let's look at how the LLM figures out what to do — and where it struggles.

Attempt 1: Listing Sales Orders

The LLM's first approach to the turnover question:

  1. Tried to list available Saved Searches
  2. Attempted a SuiteQL query against the Transaction table — malformed syntax
  3. Refined the query — still malformed
  4. Refined again — still broken
  5. Gave up on SuiteQL and fell back to a Saved Search, guessing the right one from its title

Result: wrong answer, and a lot of wasted calls to NetSuite.

Attempt 2: Same Question, New Session

Starting fresh, the LLM took a different path:

  1. Listed available Saved Searches
  2. Built and ran a SuiteQL query against the Transaction table
  3. Got the results directly

Result: right answer. The LLM was already learning what works.

Attempt 3: Sales Rep Performance

When I asked for sales rep performance data, things got harder:

  1. The LLM attempted a SuiteQL query with a join, partially guessing the correct field — got an error
  2. It asked me for more information to refine the query. I replied like any normal user would: "It's your problem — how am I supposed to know the fields?"
  3. It tried a workaround query to learn from the underlying schema structure, but then timed out after 5 minutes (too many results)

Result: fail. The minimal prompt I was using wasn't enough for complex queries. The LLM needs more context about NetSuite's data model to do its job properly.

Challenges and Lessons Learned

While NetSuite's own demos show the AI Connector working smoothly with Claude and ChatGPT, my experience with Google's ADK revealed that there are still many immature pieces in this puzzle. That said, everything is moving so fast that experimenting is a must.

Here's what I found:

Smaller models don't work yet. I tested with the instruct versions of Qwen3 14B, DeepSeek R1, GPT-OSS 120B, and Mistral 7B. None could reliably use the NetSuite MCP tools. For now, you need the reasoning power of a frontier model.

Tools do their job, but ERP processes are complex. Chatting directly with an ERP is impressive as a demo but fundamentally limited for real work. You need an agentic system that properly splits and coordinates activities among different, specialised agents — one for financial queries, one for inventory, one for customer data, each with domain-specific context.

The full tech stack is still maturing. As of this writing, trying to do something beyond the standard Claude/ChatGPT demos with tools like Google's ADK introduces a series of small technical hiccups. Even N8N has open pull requests for proper OAuth2 authorisation management with MCP servers.

The Bigger Picture

We're still early in the "AI agents for enterprise" era. The frameworks exist. The protocols exist. But the integration patterns are still being discovered.

Projects like this — where you hit real problems and share real solutions — move the ecosystem forward. NetSuite's non-standard OAuth isn't a secret anymore. The mcp-remote approach is documented. The authentication trade-offs are clearer.

Agentic AI implies a lot of experimentation to get agents working properly. Sharing is essential: that's how we mature this space. Not by pretending integration is trivial, but by honestly sharing what works, what doesn't, and why.

And sometimes, the entire solution is just one word: coerce.

Code:

Documentation:

Previous article:

Originally published on Medium.

This is the kind of enterprise AI integration work we do at RAAS Impact every day — connecting AI to the systems that actually run your business. If you're exploring AI agents for NetSuite or other ERP platforms, let's talk about what a proof of concept looks like for your setup.