Skip to main content
Glama
acroston1

Epicor MCP OSS

by acroston1

Epicor MCP OSS

An Apache-2.0 read-only MCP server for Epicor Kinetic. It lets MCP-compatible assistants discover tables and fields, run bounded SELECT queries and saved BAQs, resolve dashboards, and search documents you import. It never writes business records.

You need a dedicated Epicor service account (username and password) and an Epicor REST API key for this server to work; it authenticates to Epicor as that account on behalf of every client. This project is independently developed and is not an official Epicor product and not endorsed by Epicor.

Microsoft SSO is disabled by default. Administrators can expose an explicit list of read-only tables, optionally require a shared server token, and enable Microsoft SSO later. Every tool call is recorded in a local audit log. No Epicor table/field database, vector index, vendor manuals, tenant configuration, credentials, or business records are included.

Tool surface

Tool

Purpose

epicor_query

Execute a bounded SELECT or read an existing saved BAQ

epicor_tables

Find tables in your imported metadata (substring, or optional semantic ranking)

epicor_fields

Find fields and descriptions for an allowed table (substring, or optional semantic ranking)

epicor_help

Search your imported documents; optional semantic search

epicor_dashboards

Resolve dashboard names to saved BAQ IDs

In SSO-disabled mode, all clients share the same table policy and service account. Business-record writes, BAQ saving, and admin endpoints are disabled. A table whitelist restricts data access; it does not authenticate a caller. For hosted use, set a shared server token or place the service behind your own access-controlled network gateway.

Related MCP server: tableau-graphql-mcp

1. Install

Use Python 3.11 or newer, an Epicor Kinetic instance with REST API v2 enabled, a dedicated Epicor service account, and an API key with the necessary access scope. No GPU or embedding service is required for the default configuration.

From the repository root on Linux/macOS:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e .
cp .env.example .env
cp table_whitelist.example.txt table_whitelist.txt

On Windows, use python -m venv .venv, activate .venv\Scripts\Activate.ps1, and use Copy-Item instead of cp. Keep .env, imported documents, and all generated files private; they are excluded from version control.

2. Configure Epicor and read-only access

Edit .env. These are the required connection settings:

EPICOR_MCP_AUTH_MODE=none
EPICOR_MCP_ENVIRONMENT=live
EPICOR_MCP_EPICOR_LIVE_URL=https://epicor.example.com/YourInstance/api/v2/odata/YOUR_COMPANY/
EPICOR_MCP_EPICOR_COMPANY_ID=YOUR_COMPANY
EPICOR_MCP_EPICOR_USERNAME=your-service-account
EPICOR_MCP_EPICOR_PASSWORD=your-password
EPICOR_MCP_EPICOR_API_KEY=your-epicor-api-key
EPICOR_MCP_TABLE_WHITELIST_PATH=table_whitelist.txt

The three credential values can instead live in a private INI file: copy .epicor_credentials.example to .epicor_credentials, fill it in, and set EPICOR_MCP_CREDENTIALS_PATH=.epicor_credentials. Values set in .env take precedence over the file.

The URL is the full company OData base, including your installation's path, api/v2/odata, and company ID. The server does not substitute a company ID into the URL. To use pilot, set EPICOR_MCP_ENVIRONMENT=pilot and configure EPICOR_MCP_EPICOR_PILOT_URL. Settings and relative data paths resolve from the working directory; run commands from the repository root.

Query execution uses Epicor's BAQ parsing/execution APIs, which use HTTP POST even for reads. Configure your API access scope for the operations you intend to use, including:

  • Ice.BO.BAQDesignerSvc: ParseFromSQL, GetTableList, and GetFieldList for parsing and schema inspection.

  • Ice.BO.DynamicQuerySvc: Execute, Analyze, and GetByID for query execution, diagnostics, and saved BAQ definition inspection.

  • BaqSvc: execution of permitted saved BAQs.

  • Ice.BO.DashBoardSvc: read access if dashboard lookup is needed.

Do not grant broad write access just because read methods use POST. Epicor service-account permissions and API access scopes apply in addition to this server's table policy. EPICOR_MCP_EPICOR_BAQ_API_KEY optionally selects a separate BAQ key; otherwise it uses EPICOR_MCP_EPICOR_API_KEY. The default mode never creates or overwrites saved BAQs, even if the API key could do so.

Checklist to hand your Epicor administrator. Creating these needs Epicor security rights. Screen names are from Epicor Kinetic and may be labelled slightly differently in your version:

  1. A dedicated service user, created in User Account Security Maintenance, with access to the company you will query.

  2. A REST API key from API Key Maintenance. Epicor shows the key only once; put it in .env or .epicor_credentials.

  3. An access scope from Access Scope Maintenance, assigned to that key and allowing only the services listed above.

  4. The company's REST v2 OData base URL, https://<server>/<instance>/api/v2/odata/<company>/, and the company ID.

Edit table_whitelist.txt to list only tables you intend to expose:

# Example: part master and warehouse balances
Erp.Part
Erp.PartWhse

Names are case-insensitive. Schema-qualified entries are exact; a bare Part entry means Erp.Part. Erp.Part does not grant Ice.Part.

The whitelist applies to table discovery, field discovery, ad-hoc SQL, joined and nested query tables, and the definitions behind saved BAQs. Listing a base table does not automatically allow its _UD mirror. Wildcards are rejected. For ad-hoc SQL the check runs on Epicor's parsed statement: the SQL text goes to ParseFromSQL, which reads no business data, and a non-whitelisted table is refused before anything executes. The built-in sensitive-table/column denylist always takes precedence. Add further denials in an optional table_blacklist.txt.

An empty whitelist denies all table access. A missing or malformed configured file stops startup. Restart after editing either list. To explicitly opt out of the whitelist, set EPICOR_MCP_TABLE_WHITELIST_PATH=; the remaining limits are the built-in denylist and your Epicor credentials' permissions.

For a hosted endpoint without Microsoft SSO, also set:

EPICOR_MCP_SERVER_TOKEN=replace-with-a-long-random-secret
EPICOR_MCP_RESPONSE_PUBLIC_BASE_URL=https://mcp.example.com

You can generate a shared token with python -c "import secrets; print(secrets.token_urlsafe(32))". This token is separate from your Epicor API key. Clients receive only the server token; the Epicor credentials stay on the server.

3. Build local table/field metadata

The server starts without a bundled schema corpus; table/field search is empty until you import metadata. For accurate SQL discovery, read physical table and field definitions from your own Epicor instance:

python scripts/build_schema_catalogue.py --output-dir data

This explicit command calls GetTableList and GetFieldList using .env credentials. It changes no Epicor records and saves no BAQ. It writes data/schema_catalogue.json, data/baq_schema.db, and data/discovery_index/, using metadata and substring search without vectors. Local SQL column validation also reads data/schema_catalogue.json automatically, including field types, so no separate validator cache is needed. Use --tables Part,PartWhse to build a selected subset; that output replaces the previous catalogue with the selected subset. Rebuild after schema changes.

This is also your first live check of the Epicor connection, because /health does not log in to Epicor. Start with one table:

python scripts/build_schema_catalogue.py --output-dir data --tables Part

Success proves the URL, service account, API key, and the key's access scope for Ice.BO.BAQDesignerSvc. A name-resolution or connection error means the URL or network; HTTP 401/403 means the credentials or access scope (see section 2). Then rebuild with the tables in your whitelist, or omit --tables for the full catalogue; a full build reads metadata for every table, so it takes longer.

If you already have your own Epicor Swagger/OpenAPI JSON exports, you can build a local service index without network access:

python scripts/bootstrap_schema.py --swagger-dir /path/to/swagger-json --output-dir data

Use JSON files exported for your Epicor services, with info, paths, and OpenAPI components.schemas or Swagger definitions. BO/REST projections can contain fields that do not exist in SQL. This fallback labels discovery as unverified; the physical-catalogue command above is the recommended source for SQL. To use an existing authoritative physical catalogue alongside Swagger:

python scripts/bootstrap_schema.py --swagger-dir /path/to/swagger-json \
  --catalogue /path/to/schema_catalogue.json --output-dir data

Missing, malformed, or Swagger-only metadata cannot prove that a physical SQL column is absent, so local column validation abstains. Query parsing and table access checks still run. For a physical catalogue outside data/, set the process environment variable EPICOR_MCP_SCHEMA_CATALOGUE before starting the server. EPICOR_MCP_COLUMN_CATALOGUE optionally overrides only column preflight with a list of catalogue paths, separated by : on Linux/macOS or ; on Windows. These two advanced overrides are read from the process environment, not .env; the default commands above need neither. Discovery still uses the generated discovery index configured for the server.

Optional candidate-key metadata can improve join/fan-out diagnostics for tables beyond the small core map. Set EPICOR_MCP_TABLE_KEYS_PATH=data/table_keys.json and supply your own definitions, for example:

{"tables": {"Part": [["Company", "PartNum"]]}}

Each nested list is a complete candidate key, not an arbitrary list of useful columns. Key metadata informs diagnostics; it never grants table access.

Optionally set a JSON map of your site (Plant) codes to names in .env or the process environment. The server includes these operator-provided hints in MCP initialization instructions so the model can use the correct codes in a Plant filter. The server ships no site map:

EPICOR_MCP_PLANTS={"10": "Main Plant", "20": "North Plant"}

Omit the setting, leave it blank, or use {} to provide no hints. Codes and names must be non-empty strings; invalid maps fail configuration validation. The map does not grant table access or rewrite SQL. Restart the server and reconnect clients after changing it.

Only import schema exports and documentation you are entitled to use. None of these generated files belong in a public repository.

Optional: semantic search for tables and fields

Skip this if substring search over names, labels and descriptions is enough. To rank tables and fields by meaning ("suppliers we sent orders to" finds POHeader before OrderHed), rebuild the discovery index with vectors from an embedding model you choose. Two providers are supported, and the index manifest records which one built it.

A local Sentence Transformers model (the same interface as document search):

python -m pip install -e '.[semantic]'
python scripts/build_discovery_index.py --model /path/to/your-embedding-model \
  --local-files-only

An OpenAI-compatible /v1/embeddings server you operate yourself (vLLM, Ollama, llama.cpp and similar; nothing is contacted unless you pass this):

python -m pip install -e '.[vectors]'
python scripts/build_discovery_index.py \
  --endpoint http://localhost:8000/v1/embeddings --model your-model --dim 1024

Then configure the server with the same provider, model and dimension and turn the switch on:

EPICOR_MCP_VECTOR_SEARCH_ENABLED=true
# Local model:
EPICOR_MCP_EMBEDDING_MODEL=/path/to/your-embedding-model
# Or endpoint:
EPICOR_MCP_EMBED_ENDPOINT=http://localhost:8000/v1/embeddings
EPICOR_MCP_EMBED_MODEL_NAME=your-model
EPICOR_MCP_EMBED_DIM=1024

Restart the server. Every epicor_tables and epicor_fields response states its search_mode. If the switch is off, the configured model or dimension differs from the build, the arrays are missing or corrupt, or the provider is unreachable, discovery falls back to substring search and says why under notes. Rebuild after schema or model changes; a rebuild without --model removes the vectors again. The endpoint provider embeds each query over the network at request time; the local provider loads model files only.

4. Optional: add documentation

Skip this entire section if you only want Epicor data tools. epicor_help will explain that no documents have been imported; the other tools remain available.

Put your documents in a private directory. Supported inputs are UTF-8 .txt, .md, .rst, and JSON records containing title and content (optional source_type, section, url). For text-based PDFs, first install the extra:

python -m pip install -e '.[documents]'

Import and chunk the documents:

python scripts/build_docs_index.py --input-dir /path/to/your-documents \
  --output data/epicor_docs.db

Restart the server. No embedding model is needed: help search uses case-insensitive literal substring matching. Search for distinctive words or phrases that occur in your documents. PDF page numbers are retained; scanned image-only PDFs require OCR before importing. All clients who can use this MCP can search the imported documents; the table whitelist does not partition docs. Re-running ingestion replaces the document database.

Optional: semantic search with your chosen embedding model

This additional step is optional. The supported model interface is Sentence Transformers: choose a compatible model identifier or a local model directory. The repository ships no model and prescribes no vendor.

python -m pip install -e '.[semantic]'
python scripts/build_document_vectors.py \
  --database data/epicor_docs.db --output data/document_vectors \
  --model /path/to/your-embedding-model --device cpu --local-files-only

Alternatively, pass a model identifier and omit --local-files-only to allow that explicit build to download it. --device cuda may be used with a suitable PyTorch/CUDA installation. Configure the server with the same identifier or model path used when building:

EPICOR_MCP_VECTOR_SEARCH_ENABLED=true
EPICOR_MCP_EMBEDDING_MODEL=/path/to/your-embedding-model
EPICOR_MCP_DOCUMENT_VECTORS_PATH=data/document_vectors

Restart the server. The index manifest records the model, embedding dimension, and document fingerprint. Query-time loading uses an existing local/cached model; it does not automatically download one. If vectors/model are absent, incompatible, or stale, help falls back to substring search and reports the fallback. Rebuild vectors after changing documents or models. Set EPICOR_MCP_VECTOR_SEARCH_ENABLED=false to use substring search exclusively. Table/field discovery has its own optional vector build (see section 3); the same switch enables both, but each index is built and verified separately.

5. Run and connect

epicor-mcp
# Equivalent: python -m epicor_mcp.server
curl http://localhost:8015/health

The HTTP MCP endpoint is http://localhost:8015/mcp. /health reports the active authentication and table-policy modes; it does not verify Epicor login. The server binds to loopback by default. For remote use, put an HTTPS reverse proxy in front of port 8015 and set EPICOR_MCP_RESPONSE_PUBLIC_BASE_URL to your public origin. A dedicated hostname avoids path-prefix configuration. A sample nginx configuration is in deploy/nginx.conf.example. The sample is documentation; no services or system configuration are installed.

Every tool call is appended to a local SQLite audit log, data/audit.db by default (EPICOR_MCP_AUDIT_LOG_PATH): timestamp, tool name, arguments, outcome, duration and the caller. Without Microsoft SSO there is no per-person identity, so the caller is recorded as shared-read-only; a shared server token identifies the deployment, not a user. Set EPICOR_MCP_AUDIT_LOG_ENABLED=false to turn the log off; /health reports audit_log. The file is private operator data and is gitignored.

For a compatible client with direct Streamable HTTP support, use the full https://mcp.example.com/mcp endpoint and, when configured, send Authorization: Bearer YOUR_SHARED_SERVER_TOKEN.

Connect your coding agent

Start the server from the repository root with epicor-mcp, then point your client at http://127.0.0.1:8015/mcp (or your public /mcp URL). Prefer this HTTP endpoint: the server reads .env and data/ from its working directory, and clients that launch stdio servers start them somewhere else. If you set EPICOR_MCP_SERVER_TOKEN, export the same value in the client's environment and keep the header lines below; without a server token, drop them.

Claude Code

claude mcp add --transport http epicor http://127.0.0.1:8015/mcp \
  --header "Authorization: Bearer $EPICOR_MCP_SERVER_TOKEN"
claude mcp list

The default scope stores the entry in ~/.claude.json. With --scope project it goes to .mcp.json in your project; write the header there as "Bearer ${EPICOR_MCP_SERVER_TOKEN}" so the token itself is not committed.

Codex CLI (config.toml in the .codex folder of your home directory)

[mcp_servers.epicor]
url = "http://127.0.0.1:8015/mcp"
bearer_token_env_var = "EPICOR_MCP_SERVER_TOKEN"

Or run codex mcp add epicor --url http://127.0.0.1:8015/mcp --bearer-token-env-var EPICOR_MCP_SERVER_TOKEN, then check with codex mcp list.

Opencode (opencode.json)

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "epicor": {
      "type": "remote",
      "url": "http://127.0.0.1:8015/mcp",
      "enabled": true,
      "headers": {"Authorization": "Bearer {env:EPICOR_MCP_SERVER_TOKEN}"}
    }
  }
}

Check with opencode mcp list.

Claude Desktop cannot use a localhost URL as a custom connector, because those connections come from Anthropic's cloud rather than your machine, and its local-server configuration has no working-directory setting. Use the desktop connector in bridge/README.md: add it to claude_desktop_config.json (macOS ~/Library/Application Support/Claude/, Windows %APPDATA%\Claude\), then fully quit and reopen the app.

Stdio instead of HTTP. Codex (cwd) and Opencode (cwd) can start the server in the repository root: use the absolute path /path/to/epicor-mcp-oss/.venv/bin/epicor-mcp with the argument --stdio. For Claude Code, wrap it: claude mcp add --transport stdio epicor -- sh -c 'cd /path/to/epicor-mcp-oss && exec .venv/bin/epicor-mcp --stdio'.

First success check: ask the agent to call epicor_tables for "part". After the section 3 metadata build it lists matching whitelisted tables; before it, the result is empty and explains why.

For desktop clients that need a local executable, see bridge/README.md for complete build and configuration steps. Build on Windows with Python and PyInstaller:

cd bridge
python -m venv .buildvenv
.\.buildvenv\Scripts\Activate.ps1
.\build_exe.ps1

Distribute bridge/dist/epicor_mcp_oss_bridge.exe. Users configure MCP_SERVER_URL, MCP_AUTH_MODE=none or token, and optionally MCP_SERVER_TOKEN. The executable does not contain your server credentials and does not need rebuilding when its server URL changes. Linux/macOS builds use bridge/build_exe.sh and produce a native executable for that OS.

Direct local stdio is also available with epicor-mcp --stdio; it uses the same SSO-disabled table policy and local server configuration. Microsoft SSO, when enabled, requires the HTTP server and OAuth-capable connector. See docs/microsoft-sso.md.

Troubleshooting and development

  • Startup names missing settings: fill in .env and check the working directory.

  • Startup rejects the whitelist: copy the example, use one table name per line, and remove wildcard expressions. A blank file intentionally grants nothing.

  • Tables/fields are empty: build your local metadata and restart; ensure the desired tables are allowed.

  • Epicor returns 401/403: check service-account permissions, company URL, and the API key's method scope. A shared MCP token does not replace Epicor auth.

  • A query returns epicor_unreachable or epicor_auth_error: the server could not reach Epicor or Epicor rejected its credentials. Fix the configuration and restart; rewriting the SQL will not help.

  • Help has no results: import documents and try a literal phrase from the text.

  • Remote client gets a host/origin error: match the public URL setting to your proxy hostname; configure EPICOR_MCP_MCP_CLIENT_ORIGINS for your client.

  • Bridge cannot start: run --check with its environment variables set. Windows executable builds must be performed on Windows.

Run the deterministic suite with no live Epicor access:

python -m pip install -e '.[dev]'
.claude/e2e-gate.sh
# Windows: python -m pytest tests -q

The repository keeps the established engine modules even where they are not registered as public tools. The supported public surface is the five tools listed above. Each package carries a short CLAUDE.md/AGENTS.md stating its public interface and invariants for contributors and coding agents.

Start with the repository guide for the directory map and public design for the architecture and authorization boundaries. The offline gate also runs python scripts/check_repo_docs.py to check local Markdown links, quoted repository file references in prose and Python comments/ docstrings, required guide pairs, their 60-line limit, and retired references. Generated data paths in setup examples are operator inputs/outputs, not bundled files.

License

The framework is licensed under the Apache License 2.0. See NOTICE. Imported Epicor documentation, schema exports, embedding models, and third-party dependencies retain their own terms; they are not relicensed by this repository. Epicor MCP OSS is independently developed and is not an official Epicor product, nor endorsed by Epicor.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    This project builds a read-only MCP server. For full read, write, update, delete, and action capabilities and a simplified setup, check out our free CData MCP Server for Epicor Kinetic (beta): https://www.cdata.com/download/download.aspx?sku=UEZK-V&type=beta
    3
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Enables querying Tableau lineage through the Metadata API via MCP, supporting impact analysis, where-used lookups, and content search. Provides read-only tools for answering natural-language questions about workbooks, datasources, fields, and dependencies.
    7
    1
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables querying Proluxe ERP data such as customers, orders, invoices, AR aging, bills of materials, product catalog, focus priorities, and projects through MCP tools.
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables MCP clients to interact with Exact Synergy Enterprise and Exact Globe+ installations, discovering entities and public actions from the OData metadata and safely performing validated reads (with optional allowlisted mutations).
    8
    26 npm
    MIT