Skip to content

How to build an MCP server for your ERP (read-only first)

A step-by-step guide to exposing ERP data to AI agents through the Model Context Protocol: narrow tools, read-only credentials, validated inputs, and a safe path to writes.

, Founder, Poligon Tech 4 min

Short answer

An MCP server is a small program that exposes a set of tools an AI agent can call. To connect an agent to your ERP, write a server with a few narrow, read-only tools such as get_order or get_stock_level, run it with a database user or API key that can only read, validate every input, and log every call. Add write tools later, and have them create drafts a person approves rather than change records directly.

Every operations team we talk to wants the same thing from AI: look this up in the ERP for me. What is the status of this order? Do we have stock of this SKU in the other warehouse? What did this customer pay last time?

The cleanest way to give an agent that ability today is an MCP server: a small program that wraps your ERP in a handful of well-defined tools. This guide shows how to build one safely, in TypeScript, starting read-only.

What MCP is, in one paragraph

The Model Context Protocol is an open standard for connecting AI applications to tools and data. You write a server that declares tools, each with a name, a description and an input schema. An MCP client (Claude Code, the Claude desktop app, and a growing list of other agent tools) connects to it, shows the tools to the model, and runs them when the model asks. You write the integration once and every compatible agent can use it.

Step 1: Pick three questions, not thirty

Start from what people actually ask, not from the ERP’s schema. For an order desk, the first three are usually:

  1. What is the status of order X?
  2. How much stock of SKU Y do we have, and where?
  3. What has customer Z ordered recently, and at what price?

Each becomes one tool. Resist the urge to expose everything. Each tool is something you must secure, test and explain to the model.

Step 2: Give the server read-only access

Before writing code, create the credentials the server will use:

  • A dedicated database user or API key for the agent, never a person’s account.
  • Read-only, and ideally limited to a few views rather than whole tables.
  • No access to fields the agent doesn’t need: personal data, salaries, bank details.

This is the most important step. If the model is tricked or simply wrong, the damage is limited to what these credentials can do.

Step 3: Write the server

Install the official TypeScript SDK and a validation library:

npm install @modelcontextprotocol/sdk zod

Then a minimal server with one tool:

// erp-server.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import { erp } from './erp-client.js'; // your thin wrapper around the ERP's API or read-only views

const server = new McpServer({ name: 'erp-readonly', version: '0.1.0' });

server.registerTool(
  'get_order',
  {
    title: 'Get sales order',
    description:
      'Look up one sales order by its number (format SO-123456). Returns status, lines, quantities, prices and promised delivery date. Read-only.',
    inputSchema: { orderNumber: z.string().regex(/^SO-\d{6}$/) },
  },
  async ({ orderNumber }) => {
    const order = await erp.getOrder(orderNumber);
    if (!order) {
      return { content: [{ type: 'text', text: `No order found with number ${orderNumber}.` }], isError: true };
    }
    return { content: [{ type: 'text', text: JSON.stringify(order) }] };
  },
);

await server.connect(new StdioServerTransport());

A few details in there matter more than they look:

  • The description is the documentation the model reads. Say what the tool returns, what format the input takes, and that it is read-only.
  • The input schema is a guardrail. The regular expression means the model cannot pass anything except a well-formed order number.
  • Errors are answers. Returning “No order found” with isError lets the model recover (“Let me check the number with the customer”) instead of crashing the loop.

The SDK API evolves; check the MCP documentation for the current version. Python has an equivalent official SDK if your team prefers it.

Step 4: Shape the output for the model

ERP records are wide. A raw order row can have a hundred columns, most of them internal. Return what a person would need to answer the question:

// in erp-client.ts
export function toAgentOrder(row: ErpOrderRow) {
  return {
    number: row.DOC_NO,
    customer: row.CUST_NAME,
    status: row.STATUS_DESC,
    promisedDate: row.PROM_DT,
    lines: row.lines.slice(0, 50).map((l) => ({ sku: l.ITEM, qty: l.QTY, unitPrice: l.NET_PRICE })),
  };
}

Smaller, cleaner output means fewer tokens, lower cost, and fewer chances for the model to misread a field. Cap list sizes so one call can never return ten thousand rows.

Step 5: Log every call

Wrap each tool handler so it records the tool name, inputs, a hash or summary of the output, the duration, and which agent session called it. When someone asks “why did the agent say this order was shipped?”, you want the answer in seconds.

Step 6: Connect it and test with real questions

In Claude Code, register the server:

claude mcp add erp -- npx tsx erp-server.ts

Or commit a .mcp.json to the repository so your team shares the configuration. Then ask it the questions from Step 1, using real order numbers, and read the tool calls it makes. If it picks the wrong tool or passes the wrong input, the fix is almost always a clearer tool description, not a cleverer prompt.

Step 7: Add writes, carefully

Read-only covers a surprising amount of value. When you do need writes, keep two rules:

  1. Write tools create drafts, not records. create_draft_order puts a proposed order in a review queue; a person approves it, and only then is it posted to the ERP. See our guide to human-in-the-loop approvals.
  2. Business rules live in the tool, not the prompt. If prices can’t change by more than a set percentage without a manager, the tool rejects the draft. The model can’t talk its way around a check in code.

Use a separate credential for the write path, with permission only to create drafts.

A checklist before production

  • Dedicated, least-privilege credentials, separate for read and write
  • Narrow tools named after business actions, not run_sql
  • Validated inputs and capped outputs
  • Personal and financial fields excluded unless the task needs them
  • Every call logged with the session that made it
  • Writes go through a draft and a human approval
  • A set of real past questions you rerun after every change

That last point is the one teams skip. A dozen real cases with known answers, rerun whenever a tool, prompt or model changes, catches most regressions before your operators do.

Where this fits

An MCP server is one part of an agent’s harness: the part that decides what the agent can touch. It is also why agents don’t require an ERP migration. The server adapts to the ERP you have, through the APIs, views or exports it already offers.

If you would rather not build this yourself, connecting agents to existing systems with exactly these boundaries is a large part of what we do. It usually starts with a workflow audit.

Frequently asked questions

What is an MCP server?

MCP (Model Context Protocol) is an open standard for connecting AI applications to tools and data. An MCP server is a program that exposes tools, and optionally resources and prompts, over that protocol. Any MCP-compatible client, such as Claude Code, the Claude apps or many other agent tools, can then use those tools without custom integration code.

Is it safe to connect an AI agent to an ERP?

It can be, if the safety lives in the server and the credentials rather than in the prompt. Use a read-only account for lookups, expose narrow tools instead of raw SQL, validate inputs, cap result sizes, log every call, and route any write through a draft-and-approve step.

Should I expose raw SQL to the agent?

Not in production. A run_sql tool lets the model read anything the account can read and makes calls hard to audit. Narrow tools named after business actions are safer, easier for the model to use correctly, and much easier to review.

Do I need to replace my ERP to use AI agents?

No. An MCP server sits on top of the systems you already run and calls their existing APIs, views or exports. The ERP stays the source of truth.

Keep reading

Guides

Why run an AI agent on a server (and how to do it safely)

Running Hermes, OpenClaw, T3 Code or Claude Code on an always-on server makes agents more useful and, done right, safer. When it is worth it, which setup fits which harness, and a security checklist.

Engage

Bring one workflow. We'll bring the engineers.