@suiteinsider/netsuite-mcp
Version:
NetSuite MCP server with OAuth 2.0 PKCE authentication. Works seamlessly with Claude Code, Cursor IDE, and other MCP clients.
421 lines (320 loc) • 12.9 kB
Markdown
# NetSuite MCP Server
A Model Context Protocol (MCP) server providing access to NetSuite data through OAuth 2.0 with PKCE authentication. Works seamlessly with any MCP-compatible client including Claude Code, Cursor IDE, and Gemini CLI.
## Features
- ✅ **OAuth 2.0 with PKCE** - Secure authentication without client secrets
- ✅ **Automatic Token Refresh** - Tokens refresh automatically before expiration
- ✅ **Environment Variable Support** - Configure credentials once in your MCP config
- ✅ **Session Persistence** - Authentication survives server restarts
- ✅ **Universal MCP Integration** - Works with Claude Code, Cursor IDE, Gemini CLI, and other MCP clients
- ✅ **NetSuite MCP Tools** - Access to all NetSuite MCP capabilities (SuiteQL, Reports, Saved Searches, etc.)
- ✅ **Modular Architecture** - Clean, maintainable codebase following single-responsibility principle
## Quick Start
### 1. NetSuite Setup
#### Step 1: Install NetSuite AI Connector SuiteApp
Before creating the integration record, you must install and configure the NetSuite AI Connector SuiteApp:
**Important**: The NetSuite AI Connector SuiteApp is required for MCP functionality. Without it, the MCP tools will not be available even after authentication.
#### Step 2: Create OAuth Integration Record
After installing the SuiteApp, create an integration record:
1. Navigate to **Setup > Integration > Manage Integrations > New**
2. Fill in the details:
- **Name**: "MCP Server Integration"
- **OAuth 2.0**: Checked Authorization Code Grant
Checked Public Client
- **Redirect URI**: `http://localhost:8080/callback` (or your custom port)
3. Save and copy the **Client ID** (consumer key)
**Note**: we dont need client secret (since this is public client and Authorization Code Grant with pkce)
<img width="1891" height="410" alt="image" src="https://github.com/user-attachments/assets/1779d97e-77e2-4968-8a59-d814e99a8492" />
### 2. MCP Client Configuration
Add to your MCP client's configuration file:
**Claude Code**: `~/.claude.json`
**Cursor IDE**: `.cursor/mcp.json`
**Gemini CLI**: Per Gemini's MCP setup
#### Option A: Using npx (Recommended - No Installation Required)
```json
{
"mcpServers": {
"netsuite": {
"command": "npx",
"args": ["@suiteinsider/netsuite-mcp@latest"],
"env": {
"NETSUITE_ACCOUNT_ID": "your-account-id",
"NETSUITE_CLIENT_ID": "your-client-id",
"OAUTH_CALLBACK_PORT": "8080"
}
}
}
}
```
**Benefits**:
- No manual installation required
- Always uses the latest version with `@latest`
- Clean, simple configuration
- Works immediately after MCP client restart
**Optional Environment Variables**:
- `OAUTH_CALLBACK_PORT` - OAuth callback port (default: 8080)
#### Option B: Local Development Setup
For contributing or local development:
```bash
# Clone the repository
git clone https://github.com/dsvantien/netsuite-mcp-server.git
cd netsuite-mcp-server
# Install dependencies
npm install
# Test locally with npm link
npm link
```
Then configure with absolute path:
```json
{
"mcpServers": {
"netsuite": {
"command": "node",
"args": ["/absolute/path/to/netsuite-mcp-server/src/index.js"],
"env": {
"NETSUITE_ACCOUNT_ID": "your-account-id",
"NETSUITE_CLIENT_ID": "your-client-id",
"OAUTH_CALLBACK_PORT": "8080"
}
}
}
}
```
#### Option C: Without Environment Variables
```json
{
"mcpServers": {
"netsuite": {
"command": "npx",
"args": ["@suiteinsider/netsuite-mcp@latest"]
}
}
}
```
**Note**: You'll need to provide credentials when calling `netsuite_authenticate`
### 3. Authenticate & Use
Start your MCP client and authenticate:
```
Authenticate with NetSuite
```
A browser window opens → Login to NetSuite → Authentication complete!
**Important**: After authentication, you'll need to restart your chat or reconnect the MCP server to see NetSuite tools. This is normal MCP behavior.
Once authenticated, use natural language queries:
```
Show me all customers
List available saved searches
Run a SuiteQL query to get sales orders from last month
Execute the "Monthly Revenue" report
```
## Architecture
```
MCP Client (Claude Code, Cursor, Gemini, etc.)
│
│ stdio (JSON-RPC)
▼
┌──────────────────────────────┐
│ MCP Server (Node.js) │
│ │
│ ┌────────────────────────┐ │
│ │ OAuth Manager │ │
│ │ - PKCE generation │ │
│ │ - Local HTTP server │ │
│ │ (port 8080 default) │ │
│ │ - Token storage │ │
│ └────────────────────────┘ │
│ │
│ ┌────────────────────────┐ │
│ │ MCP Tools │ │
│ │ - ns_runCustomSuiteQL │ │
│ │ - ns_runReport │ │
│ │ - ns_listSavedSearches │ │
│ └────────────────────────┘ │
└──────────────────────────────┘
│
│ HTTPS + Bearer Token
▼
┌──────────────────────────────┐
│ NetSuite MCP REST API │
└──────────────────────────────┘
```
## Project Structure
```
netsuite-mcp-server/
├── src/
│ ├── index.js # Main MCP server entry point
│ ├── oauth/
│ │ ├── manager.js # OAuth flow orchestrator
│ │ ├── pkce.js # PKCE challenge/verifier generation
│ │ ├── callbackServer.js # HTTP callback server with CSRF protection
│ │ ├── sessionStorage.js # Session file management
│ │ └── tokenExchange.js # Token exchange & refresh operations
│ ├── mcp/
│ │ └── tools.js # NetSuite MCP API client
│ └── utils/
│ └── browserLauncher.js # Cross-platform browser launcher
├── sessions/ # OAuth tokens (gitignored)
├── authenticate.js # Standalone CLI authentication utility
├── package.json
├── .gitignore
└── README.md
```
### Modular Design Benefits
The codebase follows the single-responsibility principle:
- **pkce.js** - PKCE utilities (base64 encoding, challenge generation)
- **callbackServer.js** - HTTP callback handling (CSRF protection, HTML pages, timeouts)
- **sessionStorage.js** - Session persistence (save, load, clear, isAuthenticated)
- **tokenExchange.js** - NetSuite OAuth API communication (token exchange/refresh)
- **browserLauncher.js** - Cross-platform URL opening (macOS, Windows, Linux)
This modular structure enables:
- ✅ Independent testing of each module
- ✅ Easy maintenance and debugging
- ✅ Reusability in other projects
- ✅ Clear separation of concerns
## Environment Variable Configuration
### Configuration Example
**Recommended npx setup:**
```json
{
"mcpServers": {
"netsuite": {
"command": "npx",
"args": ["@suiteinsider/netsuite-mcp@latest"],
"env": {
"NETSUITE_ACCOUNT_ID": "123456-sb1",
"NETSUITE_CLIENT_ID": "your-client-id-here",
"OAUTH_CALLBACK_PORT": "8080"
}
}
}
}
```
**Local development setup:**
```json
{
"mcpServers": {
"netsuite": {
"command": "node",
"args": ["path/to/src/index.js"],
"env": {
"NETSUITE_ACCOUNT_ID": "123456-sb1",
"NETSUITE_CLIENT_ID": "your-client-id-here",
"OAUTH_CALLBACK_PORT": "8080"
}
}
}
}
```
### Environment Variables
- **NETSUITE_ACCOUNT_ID** - Your NetSuite account ID (required)
- **NETSUITE_CLIENT_ID** - Your OAuth client ID (required)
- **OAUTH_CALLBACK_PORT** - OAuth callback port (optional, default: 8080)
### Resolution Order
1. **Check arguments first**: If `accountId` or `clientId` provided as arguments, use them
2. **Fallback to environment variables**: If no arguments, use env vars
3. **Validation**: If neither source provides credentials, show error with instructions
### Security Best Practices
1. **File Permissions**: Ensure config file has restrictive permissions
```bash
chmod 600 ~/.claude.json
```
2. **No Secrets**: Client secrets not required (PKCE authentication)
3. **Local Token Storage**: OAuth tokens stored in `sessions/` directory
4. **Never Commit**: Don't commit config files with credentials to git
## Available NetSuite MCP Tools
Once authenticated, you'll have access to NetSuite's native MCP tools:
- `ns_runCustomSuiteQL` - Execute SuiteQL queries
- `ns_listAllReports` - List available financial reports
- `ns_runReport` - Execute a specific report
- `ns_listSavedSearches` - List saved searches
- `ns_runSavedSearch` - Execute a saved search
- `ns_getRecord` - Retrieve a specific record
- `ns_createRecord` - Create a new record
- `ns_updateRecord` - Update an existing record
- And more...
The exact tools available depend on your NetSuite account configuration.
## OAuth Flow
1. **Initiation**: User calls `netsuite_authenticate` with credentials
2. **PKCE Generation**: Server generates code verifier and SHA-256 challenge
3. **Authorization URL**: Server generates NetSuite OAuth URL and starts local callback server
4. **User Login**: Browser opens NetSuite login page
5. **Authorization**: User approves access
6. **Callback**: NetSuite redirects to `http://localhost:8080/callback` with authorization code
7. **Token Exchange**: Server exchanges code for access/refresh tokens (public client pattern)
8. **Session Storage**: Tokens stored in `sessions/session.json` (persists across restarts)
9. **Auto-Refresh**: Tokens automatically refresh when expiring (5-minute buffer)
## Troubleshooting
now uses absolute paths based on script location
### Issue: "Port already in use"
**Cause**: Another application using the OAuth callback port
**Solution**:
```bash
# Check what's using the port (example for port 8080)
lsof -i :8080
# Option 1: Kill the process
# Option 2: Change port via environment variable
```
Set custom port in your MCP config:
```json
{
"env": {
"OAUTH_CALLBACK_PORT": "9000"
}
}
```
**Remember to update the redirect URI in your NetSuite integration to match the new port!**
### Issue: Tools not appearing after authentication
**Cause**: MCP clients cache tool list at session start
**Solution**:
- **Restart chat** - Open new conversation
- **Reconnect MCP** - Use `/mcp` command (Claude Code)
- **Restart app** - Close and reopen your IDE
This is normal MCP behavior - tool lists are fetched once per session.
## Development
### Standalone Authentication
Test authentication without MCP client:
```bash
node authenticate.js <accountId> <clientId>
```
### Clearing Session
```bash
rm -rf sessions/
```
Or use the `netsuite_logout` tool in your MCP client.
### Viewing Logs
All server logs output to stderr. When running in MCP clients, these logs appear in the client's console/logs.
## Technical Details
### PKCE Implementation
- **Code Verifier**: 32 random bytes, base64url encoded
- **Code Challenge**: SHA-256 hash of verifier, base64url encoded
- **Challenge Method**: S256 (required by NetSuite)
### Token Exchange (Public Client Pattern)
```http
POST https://{accountId}.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code={authorization_code}
&redirect_uri=http://localhost:8080/callback
&client_id={client_id}
&code_verifier={verifier}
```
**Important**: No `Authorization` header (public client).
### Token Refresh
Tokens automatically refresh when expiring in < 5 minutes:
```http
POST https://{accountId}.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token={refresh_token}
&client_id={client_id}
```
## Prerequisites
- **Node.js** 18.0.0 or higher
- **NetSuite Account** with MCP access
- **NetSuite AI Connector SuiteApp** (Bundle ID: 522506) installed and configured
- **NetSuite Integration Record** with OAuth 2.0 and PKCE enabled
- **MCP Client** - Any MCP-compatible client (Claude Code, Cursor IDE, Gemini CLI, etc.)
## License
MIT
## References
- [Model Context Protocol Documentation](https://modelcontextprotocol.io/)
- [NetSuite OAuth 2.0 Documentation](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_158081952044.html)
- [PKCE Specification (RFC 7636)](https://datatracker.ietf.org/doc/html/rfc7636)