Skip to main content
Glama
Acumatica MCP Server

Acumatica MCP Server

Acumatica MCP Server

License: MIT Python Protocol Status

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⡖⠿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣞⡻⠉⢀⠀⠈⠳⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡏⠀⡂⣸⣦⠀⠀⠘⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⢻⠉⠻⠐⣭⡀⠀⠙⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀⠀⠀⠀⠀⢡⠤⠜⠂⠀⠀⠈⠘⠀⠀⠀⠱⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣠⠞⠁⠙⢦⣀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠣⡀⠀⠀⠀⠀⠀⠀⠀⣀⠤⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢸⡀⠀⠀⠀⠉⠳⣄⠀⠀⣹⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣠⣼⣶⣶⢒⣛⡻⢥⡤⣀⠀⢮⡗⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⢰⣶⣿⣿⣿⣾⣧⣤⣬⣤⣀⣙⣿⣤⣽⡆⠂⠂⡀⠀⠀⠀⠀⣀⠀⠠⠐⠂⠈⡋⣟⣿⣇⣠⡀⡼⣏⣉⡉⠱⣿⣿⣶⣶⠒⠒⠒⠒⠒⠒⠒⠒⠢⠤⢄⡀
⠀⠀⠀⠀⠉⠉⢛⣿⢿⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠀⠀⠀⠄⠀⠀⠀⠀⠂⠂⠀⠉⣟⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣿⣷⣶⣶⣶⣶⣶⣶⣾⣿⠿⠛⠁
⠀⠀⠀⠀⠀⠀⠈⠛⢻⣿⣿⣿⣿⡏⢁⣀⣀⣠⣤⣤⡤⠀⢀⠀⠀⢀⡀⣀⣠⣤⣿⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠿⢿⠟⠛⠋⠉⠉⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣿⣿⣿⣿⡿⠿⠛⠛⠉⠀⠀⠒⢾⠤⠤⠤⠀⠚⠛⠉⢩⠙⠛⠛⠟⠿⣿⡿⢿⡿⣿⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⣀⣿⣿⠟⢻⠁⠀⠀⢀⣀⣀⣀⣀⠤⠀⠀⠀⠀⠀⢀⣤⡤⠼⣴⣤⣤⣤⣤⣿⡿⠿⠛⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠠⢶⣶⣶⣿⣦⣤⣠⣿⣿⣶⣾⣿⣶⣿⣿⠿⠿⠛⠁⠀⠀⠀⠀⠤⠤⠶⠿⢿⣿⣿⣿⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠉⠛⠿⣿⣿⣿⣿⣿⣿⣿⣿⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⠿⠿⠿⠿⠙⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣿⠟⢿⣿⣿⣿⣿⡿⢻⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⣸⡇⠀⣿⣿⣿⣿⠟⠀⢸⠋⠀⠀⠀⠀⠠⣤⣀⣄⣴⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢀⡟⠀⢠⣿⣿⡟⠁⠀⠀⡸⠁⠀⠠⠀⠀⠁⠀⢉⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⣾⠀⠀⣼⡟⠁⠀⠀⠀⢠⡇⢀⢄⡞⠀⠀⡳⣴⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠈⠙⠚⠋⠀⠀⠀⠀⢀⣾⠇⠡⠈⠀⠀⢀⣾⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠿⡆⠀⠀⢀⣴⠏⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣷⣶⣾⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

A Model Context Protocol (MCP) server for Acumatica ERP. It lets an MCP client (Claude Desktop, or any MCP-aware agent) query and act on any Acumatica tenant's contract-based REST API through just 8 generic tools.

Acumatica's contract API is uniform: every entity (SalesOrder, Bill, Customer, StockItem, and so on) supports the same GET / PUT / DELETE verbs with OData query parameters, plus POST /{Entity}/{Action} for entity-specific actions. Instead of hand-coding hundreds of endpoints, this server exposes 8 tools that cover the entire surface (all ~119 entities in a standard tenant), plus a small catalog that teaches the model each entity's fields, key format, and actions.

Status: Beta. Battle-tested.

Table of Contents

Related MCP server: acu-mcp

Features

  • Small and legible. One server file, ~700 lines, three dependencies. You can read the whole thing before trusting it with your ERP.

  • Complete coverage. 8 generic tools reach all ~119 entities of any tenant's contract API, no per-entity code.

  • Read-only by default. Writes and deletes stay disabled until you explicitly opt in, so it is safe to point at production while you explore.

  • Portable. Works against any Acumatica instance with just a service account.

  • Clickable results. Records come back with a browser_url that links straight to the record in the Acumatica web UI.

  • Self-correcting queries. Errors return actionable hints (wrong field name, missing mandatory filter, permission gap) instead of raw HTTP 500s.

  • Repeatable workflows. Ships example skills - packaged, tested procedures (AP/AR health, three-way match, and more) that turn the raw tools into reliable, one-command operations. See Skills.

Architecture

flowchart LR
    U[You] -->|natural language| C[Claude / MCP client]
    C -->|MCP tool calls| S[acumatica-mcp-server]
    S -->|"cookie-auth REST + OData"| A[(Acumatica ERP)]
    A -->|JSON records| S
    S -->|"results + browser_url"| C
    C -->|answer| U

The server is a thin, stateless translator: MCP tool calls in, Acumatica REST calls out. All entity knowledge (fields, keys, actions) lives in a data catalog (entity_catalog.json), so the tools stay generic.

The 8 tools

Tool

HTTP

What it does

list_entities

(local)

List the entities available in the tenant, filterable by substring.

describe_entity

(local)

Call this first. Returns an entity's fields, key format, actions, and expandable sub-collections.

list_records

GET /{Entity}

Query records with OData ($filter, $select, $top, $expand, and so on).

get_record

GET /{Entity}/{key}

Fetch a single record by its key.

upsert_record

PUT /{Entity}

Create or update a record. Requires ACUMATICA_ALLOW_WRITES=1.

delete_record

DELETE /{Entity}/{key}

Delete a record. Requires ACUMATICA_ALLOW_DELETES=1.

invoke_action

POST /{Entity}/{Action}

Run an action (Release, Cancel, Confirm). Requires ACUMATICA_ALLOW_WRITES=1.

get_schema

GET /{Entity}/$adHocSchema

Discover user-defined (DAC extension) fields and view names.

See docs/USAGE.md for the field-name, key-format, $filter, and $custom rules that make queries reliable.

How a query flows

The golden rule: call describe_entity first. Most failures come from guessing field names or key formats.

flowchart TD
    Q[Need data or an action] --> LE[list_entities: find the entity]
    LE --> DE[describe_entity: fields, key format, actions]
    DE --> RW{Read or write?}
    RW -->|read| RD[list_records / get_record]
    RW -->|write| G{Gate enabled?}
    G -->|"ALLOW_WRITES / ALLOW_DELETES set"| WR[upsert_record / delete_record / invoke_action]
    G -->|not set| BL[403 blocked: read-only by default]

Skills (repeatable workflows)

The 8 tools give a model the raw verbs. Skills turn those verbs into repeatable, tested workflows: the exact queries, fallbacks, and output format for a real task, written down once so the model runs it the same correct way every time. The skills/ folder ships a starter set you can use as-is or adapt:

Skill

What it does

recent-records

Reliably find the latest / most-recent record of any entity (works around tenants that ignore $orderby).

ap-health

Accounts-payable health check: aging buckets, overdue bills, stale POs, uninvoiced receipts.

ar-health

Accounts-receivable health check: open AR, aging, unapplied payments, customer concentration.

three-way-match

Reconcile Bill vs Purchase Receipt vs PO line by line and flag price / quantity variances.

doc-doctor

Diagnose why a document is stuck or wrong (receive failures, wrong totals, holds) and propose the fix.

Each skill is a single SKILL.md with frontmatter, in the format used by Claude Code / Claude Desktop skills. Drop the folder wherever your client discovers skills (for example your project's .claude/skills/) and invoke it by name. All five are read-only reporting workflows; none of them writes to your tenant.

Requirements

  • An Acumatica instance with the contract-based REST API enabled (the default Default endpoint, for example version 24.200.001).

  • A dedicated service / integration account with the appropriate role(s).

  • Python 3.10+.

Installation

Clone the repository and install its dependencies:

git clone https://github.com/yourlastnamesoundslikeatypeofpasta/acumatica-mcp-server.git
cd acumatica-mcp-server
python -m venv .venv
# Windows:      .venv\Scripts\activate
# macOS/Linux:  source .venv/bin/activate
pip install -r requirements.txt

Configuration

The server reads its connection settings from environment variables. There are two ways to supply them, and you only need one (you never enter credentials twice):

  • Option A: the MCP client env block (recommended). Put the values in your MCP client config (see Register with Claude Desktop). Nothing is written to disk and no .env file is needed.

  • Option B: a .env file. Copy .env.example to .env next to server.py and fill it in. Your MCP client config then only needs the command/args, not the credentials.

ACUMATICA_BASE_URL=https://your-instance.acumatica.com
ACUMATICA_ENDPOINT_PATH=/entity/Default/24.200.001
ACUMATICA_USERNAME=service_account
ACUMATICA_PASSWORD=your-password
ACUMATICA_COMPANY=YourCompany

If you set both, the env block (process environment) wins and the .env file only fills in anything it did not set.

Register with Claude Desktop

Add this to your claude_desktop_config.json (full example in docs/claude-desktop-config.example.json). This is Option A: credentials live in the env block. If you use a .env file instead (Option B), drop the ACUMATICA_* credential keys from env:

{
  "mcpServers": {
    "acumatica": {
      "command": "python",
      "args": ["C:/path/to/acumatica-mcp-server/src/acumatica_mcp/server.py"],
      "env": {
        "ACUMATICA_BASE_URL": "https://your-instance.acumatica.com",
        "ACUMATICA_ENDPOINT_PATH": "/entity/Default/24.200.001",
        "ACUMATICA_USERNAME": "service_account",
        "ACUMATICA_PASSWORD": "your-password",
        "ACUMATICA_COMPANY": "YourCompany"
      }
    }
  }
}

Restart Claude Desktop, then try: "List the open sales orders modified in the last 14 days" or "Describe the Bill entity."

Authentication

Cookie-based. The server logs in on the first request, holds the session cookie, transparently re-logs in on a 401, and logs out on exit.

sequenceDiagram
    participant S as acumatica-mcp-server
    participant A as Acumatica
    S->>A: POST /entity/auth/login (first request)
    A-->>S: session cookie
    S->>A: GET / PUT / POST /{Entity}
    A-->>S: 200 + data
    Note over S,A: on 401, re-login once and retry
    S->>A: POST /entity/auth/logout (on exit)

Write safety (read-only by default)

The three mutating tools are disabled by default. Enable them deliberately via environment variables:

Variable

Enables

ACUMATICA_ALLOW_WRITES=1

upsert_record and invoke_action

ACUMATICA_ALLOW_DELETES=1

delete_record

When a mutating tool is called while disabled, it returns a 403-style envelope with a hint telling you which variable to set. No request is sent to Acumatica.

Regenerating the entity catalog

The bundled entity_catalog.json covers standard Acumatica entities. If your tenant has customizations (custom entities, extension fields), regenerate it from your tenant's OpenAPI spec:

  1. In Acumatica, open your endpoint under Web Service Endpoints and export its OpenAPI (Swagger) JSON, or GET {BASE_URL}{ENDPOINT_PATH}/swagger.json.

  2. Rebuild:

    python src/acumatica_mcp/rebuild_catalog.py path/to/your_openapi_spec.json
  3. Restart the server (the catalog is loaded once at startup).

Security notes

  • Read-only by default. upsert_record and invoke_action require ACUMATICA_ALLOW_WRITES=1; delete_record requires ACUMATICA_ALLOW_DELETES=1. Enable them only when you mean to, ideally on a sandbox tenant.

  • Use a dedicated service account scoped to only the entities/roles you need, never a real person's login. A read-only role in Acumatica is a good second layer of defense.

  • .env is git-ignored. Keep credentials out of version control; prefer passing them through your MCP client's env block.

This is not the first Acumatica-to-MCP or Acumatica-to-AI project. If this one does not fit your needs, look at these:

  • grp-mcp: a far more capable MCP server with full CRUD, four client planes, and headless ERP setup.

  • MCP4Acumatica: a remote MCP server (Cloudflare Workers) with per-user OAuth and role-based, read-only access.

  • easy-acumatica: a mature Python REST SDK (not MCP) with dynamic model generation.

  • CData Acumatica MCP Server: a read-only MCP backed by the CData JDBC driver.

This server's angle is deliberate minimalism: a small, dependency-light, self-hostable stdio server you can read top to bottom in one sitting, that works against any tenant with nothing but a service account.

License

MIT (c) Christian Zagazeta

Disclaimer

Not affiliated with or endorsed by Acumatica, Inc. "Acumatica" is a trademark of its respective owner. Use at your own risk against your own tenants.

Available Tools

8 tools
delete_recordA

Delete a record by ID or key fields. Requires ACUMATICA_ALLOW_DELETES=1.

Inputs: id (required), entity (required). Returns: object

describe_entityA

Return the full metadata for an entity: fields, key format, actions, and sub-collections.

Always call this before list_records or get_record when you are unsure of:

  • which field names are valid (use these in select= and filter= expressions)

  • how to format the id= argument for get_record / delete_record

  • which sub-collections can be passed to expand=

Args: entity: Entity name, e.g. "SalesOrder", "Bill", "Customer".

Returns one of two shapes:

Normal entity (has a key): { "entity": "SalesOrder", "fields": ["OrderType", "OrderNbr", "CustomerID", ...], # valid $select / $filter names "key_fields": ["OrderType", "OrderNbr"], # fields that make up the URL key "key_format": "Slash-separated: /", # how to build the id= string "actions": ["CancelSalesOrder", ...], "expand": ["Details", "Shipments", ...] }

Query-only entity (inquiry/summary view - no addressable key): { "entity": "AccountSummaryInquiry", "query_only": true, "note": "This entity has no addressable key - use list_records with filter= only. ...", "fields": [...], "actions": [], "expand": [] } For query-only entities: do NOT call get_record - pass at least one filter= to list_records. Calling list_records with no filter on these entities returns HTTP 500 on this tenant.

Returns {error: "Unknown entity"} if the entity is not in the catalog.

Examples: describe_entity("SalesOrder") # -> key_format: "Slash-separated: /" # -> use get_record("SalesOrder", "QT/I004264")

describe_entity("Bill")
# -> key_format: "Slash-separated: <Type>/<ReferenceNbr>"
# -> use get_record("Bill", "Bill/012979")

describe_entity("AccountSummaryInquiry")
# -> query_only: true
# -> use list_records("AccountSummaryInquiry", filter="Period eq '202506'")

Inputs: entity (required). Returns: object

get_recordA

Get a single record by its key.

Call describe_entity(entity) first to find the key_fields and key_format for this entity - the key format varies per entity and using the wrong format causes a 500 error.

Args: entity: Entity name. id: The record key - key field values joined with '/' in key order. Examples: SalesOrder -> "QT/I004264" (OrderType/OrderNbr) Bill -> "Bill/001234" (Type/ReferenceNbr) Customer -> "C000001" (CustomerID only) Invoice -> "INV/001234" (Type/ReferenceNbr) Use describe_entity() to find the exact key_fields for any entity. A GUID (the session 'id' field) also works if you have it. select / expand / custom: same as list_records.

Returns: {status, ok, data: {record}} or {status, ok: false, error} Record includes a browser_url field (for entities with a known screen ID). Render the record identifier as a Markdown hyperlink using browser_url: 050297

Inputs: id (required), entity (required), custom, expand, +1 more. Returns: object

get_schemaA

Return the entity's extension-field schema (GET /{Entity}/$adHocSchema).

Two reasons to call this:

  1. Discover user-defined extension fields (UsrXxx fields, attribute fields like AttributeCOLOR) that are NOT in the standard contract and not listed by describe_entity(). These appear nested under a view name in the response.

  2. Discover the available VIEW NAMES for this entity's graph. View names are the first part of the 'ViewName.FieldName' string needed by the custom= parameter. Standard Acumatica DAC fields (e.g. CreatedByID, LastModifiedByID) that are absent from the contract can ALSO be pulled via custom= using these view names, even though they don't appear explicitly in this schema response.

Common SalesOrder view names (confirmed working): Document - SOOrder header fields (CreatedByID, BranchID, etc.) CurrentDocument - additional header computed fields Transactions - line-level fields (on Details rows) Adjustments - payment application fields

Example workflow: 1. get_schema("SalesOrder") # identify view names 2. list_records("SalesOrder", # pull extension + standard DAC fields custom="Document.CreatedByID,Document.UsrYourCustomField")

Inputs: entity (required). Returns: object

invoke_actionA

Invoke a named action on an entity (POST /{Entity}/{Action}). Requires ACUMATICA_ALLOW_WRITES=1.

Examples: invoke_action("SalesOrder", "CancelSalesOrder", entity_record={"OrderType": {"value": "SO"}, "OrderNbr": {"value": "SO012345"}}) invoke_action("Bill", "ReleaseBill", entity_record={"ReferenceNbr": {"value": "001234"}})

Args: entity: Entity name. action: Action name (call describe_entity(entity) to see available actions). entity_record: The record the action runs against, in Acumatica's wrapped format. parameters: Action-specific parameters, if any.

Returns 204 No Content for fire-and-forget actions; 202 for long-running.

Inputs: action (required), entity (required), parameters, entity_record. Returns: object

list_entitiesA

List the Acumatica entities exposed by this tenant's OpenAPI spec.

Args: filter: Optional case-insensitive substring to narrow the list, e.g. "order" returns SalesOrder, PurchaseOrder, etc.

Returns: {count, entities: [{name, actions}]}

Tip: call describe_entity(name) to get the full field list, key format, and expandable sub-collections for any entity before querying it.

Inputs: filter. Returns: object

list_recordsA

Retrieve records from an Acumatica entity using OData query parameters.

IMPORTANT: Call describe_entity(entity) first to get the exact field names for this entity. Using a field name that doesn't exist causes a hard 500 error.

Args: entity: Entity name, e.g. "SalesOrder", "Bill", "Customer". filter: OData $filter expression, e.g. "Status eq 'Open'". Only use field names returned by describe_entity() - guessed names cause KeyNotFoundException (500). Date/time fields require datetimeoffset literal format: Date gt datetimeoffset'2026-05-01T00:00:00-04:00' Plain strings or datetime'' literals will fail. top: Max rows to return. Defaults to 50; use a smaller number when exploring. skip: Rows to skip (pagination). select: Comma-separated fields, e.g. "OrderNbr,CustomerID,OrderTotal". Only use field names returned by describe_entity() - invalid names -> 500. expand: Comma-separated sub-collections to inline, e.g. "Details,Shipments". Valid values are listed in describe_entity() under 'expand'. orderby: e.g. "Date desc". NOTE: $orderby is silently ignored by this tenant. To get the most recent records, use a date filter instead and sort client-side. Use a narrow window first - expand only if empty: Step 1: filter="Date gt datetimeoffset'T00:00:00-04:00'" Step 2: if empty, retry with today-30d, then today-90d Client-side: sort results by Date desc, then LastModifiedDateTime desc to find the single most-recent record. This resolves "last created" queries in 1-2 API calls instead of 5+. custom: Pull user-defined (DAC extension) fields not in the standard contract. Format: "ViewName.FieldName" - multiple fields comma-separated. Example: "Document.LastModifiedByID,Document.CreatedByID" To discover available view names and fields, call get_schema(entity). Common view name for header-level fields: "Document".

Returns: {status, ok, data: [records]} or {status, ok: false, error} Each record includes a browser_url field (for entities with a known screen ID) linking directly to that record in the Acumatica web UI. ALWAYS render the primary identifier (ReferenceNbr, OrderNbr, etc.) as a Markdown hyperlink using browser_url so the user can click through to audit: 050297

Inputs: entity (required), top, skip, custom, +4 more. Returns: object

upsert_recordA

Create or update a record (PUT /{Entity}). Requires ACUMATICA_ALLOW_WRITES=1.

Acumatica's contract-based API uses PUT for both create and update - the server decides based on whether key fields match an existing record.

Args: entity: Entity name. data: Body in Acumatica's {"FieldName": {"value": ...}} shape. Example: {"OrderType": {"value": "SO"}, "CustomerID": {"value": "ABARTENDE"}, "Details": [{"InventoryID": {"value": "AALEGO500"}, "Quantity": {"value": 5}}]}

Inputs: data (required), entity (required). Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.4/5.0

    Scored across 8 tools

    Disambiguation5/5

    Each tool targets a distinct operation: listing entities, describing metadata, retrieving/listing/upserting/deleting records, invoking actions, and discovering extension fields. Although describe_entity and get_schema both return metadata, their roles are clearly separated (standard contract/keys vs. view names/extension fields).

    Naming Consistency5/5

    All tool names follow a consistent verb_noun snake_case pattern (list_records, get_record, delete_record, upsert_record, invoke_action, describe_entity, get_schema, list_entities). There are no mixed conventions or vague verbs.

    Tool Count5/5

    Eight tools form a tight, well-scoped set for an entity-oriented ERP API. Each tool has a clear role, and the count is within the ideal range without redundancy.

    Completeness5/5

    The server covers the full record lifecycle (list/get/create-update/delete), named business actions, entity catalog discovery, key/field metadata, and extension-field schema discovery. This leaves no obvious dead ends for interacting with Acumatica records.

    Maintenance

    ActivityNo data
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables Claude to interact with Acumatica ERP through a remote MCP server with per-user OAuth, role-based access, and 44 tools for querying and managing ERP data.
      21
      Apache 2.0
    • F
      license
      A
      quality
      D
      maintenance
      Enables read-only analytics queries on Acumatica ERP data, including sales orders, inventory, shipments, invoices, purchase orders, customers, and OData generic inquiries.
      16
      -
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables Claude to securely interact with Acumatica ERP 2025 R2 via per-user OAuth, role-based access, and sensitive field redaction, providing 48 tools for data lookup, utility, and schema discovery.
      Apache 2.0
    • F
      license
      A
      quality
      C
      maintenance
      Exposes read-only GoCanvas API v3 endpoints as MCP tools, enabling listing and retrieval of forms, submissions, reports, and reference data.
      16
      -