Epicor MCP OSS
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Epicor MCP OSSShow me open sales orders from the SalesOrder table"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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 |
| Execute a bounded SELECT or read an existing saved BAQ |
| Find tables in your imported metadata (substring, or optional semantic ranking) |
| Find fields and descriptions for an allowed table (substring, or optional semantic ranking) |
| Search your imported documents; optional semantic search |
| 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.txtOn 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.txtThe 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, andGetFieldListfor parsing and schema inspection.Ice.BO.DynamicQuerySvc:Execute,Analyze, andGetByIDfor 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:
A dedicated service user, created in User Account Security Maintenance, with access to the company you will query.
A REST API key from API Key Maintenance. Epicor shows the key only once; put it in
.envor.epicor_credentials.An access scope from Access Scope Maintenance, assigned to that key and allowing only the services listed above.
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.PartWhseNames 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.comYou 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 dataThis 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 PartSuccess 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 dataUse 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 dataMissing, 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-onlyAn 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 1024Then 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=1024Restart 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.dbRestart 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-onlyAlternatively, 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_vectorsRestart 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/healthThe 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 listThe 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.ps1Distribute 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
.envand 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_unreachableorepicor_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_ORIGINSfor your client.Bridge cannot start: run
--checkwith 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 -qThe 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.
This server cannot be deployed
Maintenance
Related MCP Connectors
Governed data discovery, exact queries, decisions, simulations, and runtime utilities over MCP.
Read-only AgentiScript concept search, catalog, authenticity, license, and approved asset discovery.
- RumboOAuthcom.rumboar
Securely query and analyze business data, dashboards, projections, alerts, and knowledge.
Let AI agents query data and act across all your business apps via MCP.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceThis 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=beta3MIT
- AlicenseAqualityAmaintenanceEnables 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.71MIT
- FlicenseNot gradedqualityCmaintenanceEnables querying Proluxe ERP data such as customers, orders, invoices, AR aging, bills of materials, product catalog, focus priorities, and projects through MCP tools.-
- AlicenseAqualityBmaintenanceEnables 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).826 npmMIT