origo-bc-mcp-server
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., "@origo-bc-mcp-serverlist my recent sales orders from Business Central"
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.
origo-bc-mcp-server
Origo Business Central MCP server — connects AI clients (VS Code Copilot, Claude Desktop, etc.) to Business Central via the Cloud Events API.
Setting this up for the first time? Start at the installation overview — it takes four parts, two of which are inside Business Central.
Features
Area | Tools | Description |
Discovery |
| Auth check and tenant/environment/company selection |
Table metadata |
| AL table/field schema introspection |
Data records |
| Read/write any BC table via Cloud Events |
Search |
| Full-text search across common master data |
Totals & aging |
| Aggregations without pulling raw rows |
Message types |
| Generic access to any BC Cloud Event message type |
Queue |
| Manage async Cloud Event queue tasks |
Translations |
| Multi-language field translation management |
Integration timestamps |
| Track last-sync watermarks for external integrations |
Memory & config |
| Persistent notes/config stored in BC's Cloud Event Config Store |
Incoming documents |
| Upload and process incoming document attachments |
Crypto |
| AES-256-GCM encryption and base64 helpers |
Business events |
| Manage |
API/OData testing |
| Discover, inspect, and test any BC API v2.0 or custom API page endpoint — full CRUD (GET/POST/PATCH/DELETE), |
Developer Services |
| AL publish/cleanup against BC Developer Services + Automation API; Cosmo SSH unit tests |
Cosmo Alpaca |
| Cosmo Alpaca container lifecycle, feed deploy, SSH info and recovery, NST restart (Bearer; independent of BC auth) |
Skills |
| Bundled reference docs for the Cloud Events API |
Related MCP server: business-central-mcp
Installation overview
A working connection has four parts, and all of them have to be right. The most common failure is finishing Part A and assuming that is the install — Parts C and D are inside Business Central and are easy to miss.
Part | Where | What |
Your machine | Install the MCP server and configure the connection | |
Entra (Azure) | App registration with access to BC | |
Business Central | Install the Origo Cloud Events Core extension | |
Business Central | Register the app and grant it permissions |
Part A on its own gives you a server that starts, authenticates, and returns nothing useful. When you are done, run the verification sequence — each step proves one part.
Part A — Your machine
1. Prerequisites
Node.js 22+ — nodejs.org (LTS recommended)
node --version2. Install from GitHub
npm install -g github:businesscentralal/origo-bc-mcpnpm clones the repository with git, so you need access to it (an SSH key or a signed-in credential helper) before this command will succeed.
Verify:
origo-bc-mcp-server --help3. Configure the connection
Run the interactive setup wizard:
origo-bc-mcp-server setupThe wizard walks you through:
Connection type (SaaS or on-prem)
Credentials (client secret, refresh token, or web service key)
Secret storage (DPAPI on Windows, Keychain on macOS)
Connection validation
MCP client configuration (
mcp.jsonfor VS Code)Desktop shortcut (optional)
To add a single connection without running the whole wizard:
origo-bc-mcp-server add sandbox14. The settings file
Settings land in ~/.origo-bc-mcp/local.settings.json (macOS/Linux) or
%USERPROFILE%\.origo-bc-mcp\local.settings.json (Windows). A SaaS connection
looks like this:
{
"devConnection": {
"tenantId": "<entra-tenant-guid>",
"clientId": "<app-client-id>",
"authType": "s2s",
"clientSecret": "keychain:origo-bc-mcp-default-secret",
"environment": "Sandbox1",
"companyId": "Vorpun"
}
}That is everything a stdio connection needs. devConnection is the only BC
credential — the app authenticates service-to-service (S2S) with a client ID and
a secret.
companyIdmust be current. A stale company name givesCompany '<name>' not foundon the first call.Store secrets behind a prefix —
keychain:(macOS),dpapi:(Windows),env:oraes:. Never in plain text.Validate the JSON after any manual edit. The server reads this file inside a
try/catchand falls back to empty settings when it cannot parse it. No error mentions the file — you only seeNo auth contextlater, from a tool call. A single stray comma is enough. Quick check:python3 -m json.tool ~/.origo-bc-mcp/local.settings.json
About basicAuth
The setup wizard usually also writes a basicAuth block. It does not apply to
stdio connections and can be left alone:
"basicAuth": { "enabled": true, "username": "dev", "password": "<password>" }basicAuth protects the server's HTTP endpoints and the dashboard (/dashboard)
when it runs in HTTP mode. Over stdio there is no HTTP traffic and no headers are
sent, so the credentials are never used.
The one visible effect: the username becomes a label that shows up as the
principal in who_am_i. That is not a BC user and it does not exist in BC. The
real identity is user.userName in the who_am_i response — the name of the
Entra app as registered in BC.
5. Connect your MCP client
Claude Code (stdio — recommended)
Create .mcp.json in the project folder:
{
"mcpServers": {
"origo-bc-mcp-sandbox1": {
"command": "origo-bc-mcp-server",
"args": ["--stdio"]
}
}
}This is a stdio connection — the server runs as a child process, with no network service and no port. That is why it does not show up under Connections in the app: it is scoped to that folder.
To use a connection other than devConnection, set MCP_CONNECTION in an env
block. See Stdio auth for BC tools.
VS Code
The setup wizard writes VS Code's mcp.json for you. For other clients over
HTTP, see Configure an MCP client.
Part B — Entra (Azure)
An app registration must exist with:
Client ID and Tenant ID — these go into
local.settings.jsonClient secret — stored in Keychain / DPAPI / an environment variable
API permission: Dynamics 365 Business Central →
API.ReadWrite.All(Application)Admin consent granted
This is access to the door only. What the app may actually do inside BC is decided in Part D.
Part C — Origo Cloud Events Core extension
The server runs every data call and every action through the custom API that this extension publishes:
/api/origo/{bifrost|cloudEvent}/v1.0/companies(<guid>)/tasks(The server tries the bifrost group first and falls back to the legacy
cloudEvent group — cloudevent on-prem.)
Without the extension almost nothing works — not who_am_i, get_records,
list_message_types, nor any message type.
Install it in BC: Extension Management → Manage → Extension Marketplace
(AppSource), or Upload Extension for a .app file.
Silent failure warning. The standard BC API (
/api/v2.0/) keeps working perfectly without the extension. Sobc_api_requestreturns correct data whileget_recordsreturns empty results and no error at all. It looks like an empty company, not a broken install. When those two disagree, suspect the extension.
Part D — Register the app in BC and grant permissions
This is the part that gets forgotten most often.
1. Register the app
Search BC for the Microsoft Entra Applications page (formerly AAD Applications).
Create an entry with the app's Client ID
Set State = Enabled
This is inside Business Central, not the Entra portal. The app does not appear under Users — S2S apps live on their own page.
2. Grant permissions
Under User Permission Sets on that entry:
Test environments — one set is enough:
Set | Name |
| Full Access |
Production — least privilege. The base set plus whichever gates you need:
Set | Name | Opens |
| API Access | Base — always required |
| Project Posting Gate | Posting to projects |
| Warehouse Posting Gate | Picks and warehouse postings |
| Item Posting Gate | Item ledger entries |
| G/L Posting Gate | General ledger postings |
| Read-Only | Read access only |
The app also needs standard BC permissions (D365 FULL ACCESS or equivalent).
CE READ ALL ORI is not sufficient on its own — not even for pure reading.
Every call writes a record to the Cloud Event Message table, so the base access
has to be there.
3. Change Log Write Guard (writes only)
The extension protects writes with an allowlist. A write to a field that is not on it stops with:
Field "1" in table 167 must be included in the change log write guard setup
to be updated via Cloud Events.If the MCP connection needs to change particular fields, add them to the Change Log Write Guard setup in BC. This is a deliberate safety valve — do not work around it without a reason.
Verify the whole chain
Run these in order. Each step proves one part:
# | Call | Should return | Points at |
1 |
|
| Part C or D missing |
2 |
| ~22 namespaces (Help, Data, Sales, Warehouse …) | Part D — permissions |
3 |
| Real records | Part C — empty result, no error |
4 |
| The same records | Part A or B |
If step 4 returns data but step 3 does not, the extension (Part C) is missing or the permission sets are short.
What the connection can and cannot do
Can: read and write any BC table through get_records / set_records, run
~80 message types (posting sales and purchase documents, approvals, warehouse
documents, and so on), and call the standard API directly.
Cannot: run arbitrary UI actions. An action that is a button on a BC page is
only reachable if it has been published as a message type. For example, Create
Inventory Pick on a project does not exist as a message type — the only pick
action is Warehouse.Pick.Create, from a warehouse shipment. Actions like that
have to be run in the UI.
List what is available with list_message_types, and get usage details with
get_message_type_help.
Managing connections
Add a connection
Add a new connection without running the full setup wizard:
origo-bc-mcp-server add production
origo-bc-mcp-server add sandbox
origo-bc-mcp-server add # prompts for nameThis asks for connection details, validates, saves to local.settings.json, and registers the MCP entry in VS Code's mcp.json.
List connections
origo-bc-mcp-server remove # lists available connections without removing anythingRemove a connection
origo-bc-mcp-server remove productionRemoves the named connection from local.settings.json and its entry from VS Code's mcp.json. Prompts for confirmation.
Create a desktop shortcut
origo-bc-mcp-server shortcut # shortcut for default server
origo-bc-mcp-server shortcut production # shortcut for a named connectionCreates a double-clickable shortcut on your Desktop to start the server:
Windows:
.lnkfile (opens cmd)macOS:
.commandfile (executable shell script)Linux:
.desktopfile
Clean all config
origo-bc-mcp-server cleanRemoves the entire local.settings.json, all origo-bc-* entries from VS Code's mcp.json, and all desktop shortcuts. Use this to start fresh. Prompts for confirmation.
Server modes (full vs lite)
By default the server runs in full mode — all tool groups from the Features table above are registered (~89 tools). This is best for capable models (GPT-4o, Claude Sonnet, etc.) that handle large tool sets well.
Lite mode registers a reduced set (~25 tools) built around invoke_message_type as a universal entry point, plus data records, aging, period breakdown, crypto, memory, and the Cloud Events skill doc. Use this for local/smaller LLMs that get confused or slow down with too many tool definitions. The API endpoint testing tools (bc_list_api_endpoints, bc_get_api_metadata, bc_api_request) are not included in lite mode.
Enable lite mode with an environment variable:
MCP_LITE=1 origo-bc-mcp-server # macOS/Linux
$env:MCP_LITE="1"; origo-bc-mcp-server # Windows PowerShellThe startup banner confirms which mode is active:
origo-bc-mcp listening on :3000 (development, LITE)
LITE MODE: reduced tool set for local LLMsTo use lite mode with a named connection or PM2, set MCP_LITE=1 in that process's environment (e.g. ecosystem.config.cjs env block for a second PM2 app entry).
Transports: stdio (recommended) vs HTTP
Transport | When to use | Network |
stdio ( | Grok Bot / Cursor local | None — process stdin/stdout only |
HTTP (default) | Local dashboard, health checks, Docker on a private host | Binds |
Remote HTTP url MCP from Cursor’s backend cannot reach ORI1058/E4-212 localhost. Stdio runs the server on the Grok Bot computer (Architect box), not on ORI1058.
Run stdio
origo-bc-mcp-server --stdio
# or, after build:
npm run start:stdioSame full tool set as HTTP (bc_dev_*, cosmo_*, and all tools from buildServer). Optional: MCP_LITE=1 for the reduced set.
Stdio auth for BC tools
HTTP Basic middleware does not run on --stdio. Auth is installed and re-bound as follows:
Startup —
MCP_STDIO_AUTH=1plus a process auth context fromlocal.settings.json(devConnectionorconnections[MCP_CONNECTION]).Every
tools/call— the MCP request handler is wrapped withensureAuthBoundso ALS is re-entered for that invocation (Cursor AddMcpServer can otherwise run handlers outside the startup ALS/enterWithtree).getAuthContextfallback — if ALS and the process fallback are both missing, readglobalThis(shared across duplicate ESM graphs) then rebuild fromMCP_CONNECTION/ local.settings.registerToolwrap — every tool callback is wrapped withwithStdioAuthat registration so Cursor handlers re-bind even when outside the startup ALS tree.
Source | Role |
| Loads settings |
| Used when |
| Used when |
| Optional; username becomes the stdio principal label. Credentials are not required on stdio (no HTTP headers). |
Cosmo ( | Independent — Bearer via |
Without a resolvable devConnection / named connection, who_am_i and bc_dev_* fail with No auth context — request reached a tool without authentication.
# default → local.settings.devConnection
origo-bc-mcp-server --stdio
# named connection (e.g. Cosmo Alpaca container wired as bc28-is-grok)
MCP_CONNECTION=bc28-is-grok \
MCP_LOCAL_SETTINGS_PATH=~/.origo-bc-mcp/local.settings.json \
origo-bc-mcp-server --stdioPrefer env: / env vars for secrets in local.settings (user/key, client secrets, Cosmo token).
Grok Bot / Cursor local command (mcp.json)
Prefer env for secrets (do not put tokens in tool args). Example:
{
"mcpServers": {
"origo-bc-mcp": {
"command": "origo-bc-mcp-server",
"args": ["--stdio"],
"env": {
"COSMO_BEARER_TOKEN": "<from Cosmo Alpaca session>",
"ADO_PAT": "<optional Azure DevOps PAT for bc_dev_publish_artifact>",
"GITHUB_TOKEN": "<optional for GitHub artifacts>",
"MCP_ENCRYPTION_KEY": "<64 hex chars if local.settings uses aes: secrets>",
"MCP_LOCAL_SETTINGS_PATH": "/path/to/local.settings.json",
"MCP_CONNECTION": "bc28-is-grok"
}
}
}
}Notes:
commandis resolved on the Grok Bot / Architect box (where the agent runs), not on ORI1058.Install the package on that box (
npm install -g github:businesscentralal/origo-bc-mcpor from the Azure Artifacts feed).BC tools over stdio need
devConnection(orconnections[MCP_CONNECTION]) inlocal.settings.json— see Stdio auth for BC tools.Connection secrets belong in env or
local.settings.jsonwithenv:/aes:prefixes — toolpat/tokenargs are optional overrides only.HTTP
urlpointing at ORI1058 localhost is not usable from Cursor’s remote MCP path; use stdio instead.
Start the server
origo-bc-mcp-serverExpected output:
origo-bc-mcp listening on 127.0.0.1:3000 (development)
MCP endpoint: http://localhost:3000/mcp
Dashboard: http://localhost:3000/dashboard
Health: http://localhost:3000/healthz
Bind: 127.0.0.1 (local only — set MCP_HOST=0.0.0.0 for Docker)For local HTTP only on loopback (default). Prefer --stdio for Grok Bot / Cursor local command.
Dashboard
The server includes a web dashboard at /dashboard:
Real-time logs — SSE stream with filtering, auto-scroll, clear
Active sessions — connected MCP clients
Server stats — uptime, memory, PID, Node version
Debug toggle — enable/disable
MCP_DEBUGat runtime without restartSetup UI (
/dashboard/setup) — manage connections, Basic Auth credentials, validate endpointsRestart / Stop — PM2-aware controls (in Docker containers)
The dashboard is protected by Basic Auth credentials. On first start with no config, it's open to allow initial setup.
Custom port
PORT=3001 origo-bc-mcp-server # macOS/Linux
$env:PORT="3001"; origo-bc-mcp-server # Windows PowerShellVerify
Check server health:
curl http://localhost:3000/healthzValidate BC connections:
origo-bc-mcp-server verify # all connections
origo-bc-mcp-server verify production # specific connectionCLI reference
origo-bc-mcp-server [command] [options]
Commands:
setup Guided wizard to configure connections and VS Code mcp.json
add [name] Add a single connection (streamlined)
verify [name] Validate a connection (default: all connections)
remove <name> Remove a specific connection (or list available)
shortcut [name] Create a desktop shortcut to start the server
clean Remove ALL connections, config, and shortcuts
init Create ~/.origo-bc-mcp/local.settings.json from template
Options:
--stdio MCP over stdin/stdout (recommended for Grok Bot / Cursor local)
--config <path> Start with a specific local.settings.json
--debug Verbose logging (stdio → stderr)
-h, --help Show helpConfigure an MCP client
Recommended (stdio): see Transports: stdio (recommended) vs HTTP for Grok Bot / Cursor command + args + env.
The setup wizard writes VS Code's mcp.json automatically (HTTP). For other clients using local HTTP (loopback only):
{
"servers": {
"origo-bc-mcp": {
"url": "http://127.0.0.1:3000/mcp",
"headers": {
"Authorization": "Basic <base64-encoded username:password>"
}
}
}
}Generate the Basic auth value:
echo -n 'username:password' | base64 # macOS/Linux
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("username:password")) # WindowsThe credentials are the basicAuth.username and basicAuth.password from your local.settings.json.
Update
npm install -g github:businesscentralal/origo-bc-mcpYour local.settings.json is preserved across updates.
Uninstall
Remove the npm package
npm uninstall -g origo-bc-mcp-serverRemove configuration files
Windows (PowerShell):
# Remove local settings
Remove-Item "$env:USERPROFILE\.origo-bc-mcp" -Recurse -Force -ErrorAction SilentlyContinue
# Remove MCP entries from VS Code (or use 'origo-bc-mcp-server clean' before uninstalling)macOS/Linux:
rm -rf ~/.origo-bc-mcpRemove VS Code MCP entries
Either run origo-bc-mcp-server clean before uninstalling, or manually edit your VS Code mcp.json:
Windows:
%APPDATA%\Code\User\mcp.jsonmacOS:
~/Library/Application Support/Code/User/mcp.jsonLinux:
~/.config/Code/User/mcp.json
Remove any "origo-bc-*" entries from the "servers" object.
Remove desktop shortcut (if created)
Delete the "Origo BC MCP" shortcut from your Desktop manually.
Remove stored secrets
If you used DPAPI or Keychain during setup, the encrypted values are embedded in the config files (already deleted above). Environment variables you set manually (e.g. BC_DEV_CLIENT_SECRET) should be removed separately:
# Windows — remove a user-level env var
[Environment]::SetEnvironmentVariable('BC_DEV_CLIENT_SECRET', $null, 'User')# macOS — remove Keychain entry
security delete-generic-password -a mcp-encrypted-conn -s origo-bc-mcp-default-secretTroubleshooting
Real messages from installs, and what is actually behind them:
Message | Cause | Fix |
| Cloud Events Core extension not installed | |
| App has no CE permission sets | |
| Field is not on the write allowlist | |
| Stale | |
| Extension missing — silent failure | |
|
| |
| Wrong filter syntax | Use BC format: |
| Bad or expired credentials |
|
| Connection settings wrong |
|
| npm global bin not on PATH | Restart the terminal; check |
| Node.js older than 22 | Install Node.js 22+ |
Port 3000 in use | HTTP mode port conflict | Use a different port (see above) |
SSL errors against on-prem BC | Self-signed certificate |
|
Custom config path
origo-bc-mcp-server --config /path/to/local.settings.jsonOr set MCP_LOCAL_SETTINGS_PATH environment variable.
Develop
cp .env.example .env # fill in BC_CLIENT_ID/SECRET, MCP_ENCRYPTION_KEY, ...
npm install
npm run dev # tsx watch
# or
npm run build && npm startSmoke check:
curl localhost:3000/healthz
curl localhost:3000/.well-known/oauth-protected-resourceBasic auth
Basic auth secures MCP endpoints and the dashboard. It works in all environments (local dev, Docker, production). Configure it in one of three ways:
Dashboard Setup UI — open
/dashboard/setup, fill in credentials (recommended for Docker)Environment variables — set
MCP_ADMIN_USER+MCP_ADMIN_PASSWORDat startupConfig file — set
basicAuthinlocal.settings.json:
cp config/local.settings.example.json config/local.settings.json
# edit: basicAuth.username/password + devConnection
npm run devThen call the server with Basic credentials:
curl -u admin:yourpass -X POST localhost:3000/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'The same credentials protect the web dashboard at /dashboard.
The Basic-auth connection is locked to its configured tenant (it cannot cross
tenants), exactly like x-origo-token.
devConnection supports two shapes:
On-prem (
onPrem: true+baseUrl,onPremTenant,user,key,companyId,companyName) — Basic auth against an on-prem BC REST base URL. Mirrors the legacyBC_ONPREM_*mode.bc_list_companiesreturns the configured company; data calls useBasic base64(user:key)against{baseUrl}/api/origo/cloudevent/v1.0/...?tenant=....SaaS (
tenantId,clientId,authType,clientSecretorrefreshToken,environment,companyId) — Entra.authType: "s2s"uses the client secret (the normal case);authType: "user"uses a refresh token. See Part A.4 for a complete example.
On-prem data calls (message types) are wired during tool migration; the connection, auth header (
onPremAuthHeader) and company listing are in place.
Developer Services (bc_dev_*)
Tools for AL publish/cleanup against BC Developer Services and the Automation API.
These talk to a container's {id}dev / Automation endpoints — not the Cosmo Alpaca control plane.
Tool | Purpose |
|
|
|
|
| Multipart |
| Download |
| Automation API |
| Automation API |
| Run AL unit tests via Cosmo SSH ( |
developerBaseUrl: set optional developerBaseUrl on devConnection, or derive it from on-prem baseUrl by replacing a trailing rest with dev (Alpaca: …/f0a4d51d4d47rest → …/f0a4d51d4d47dev). After cosmo_get_container, wire the derived restBaseUrl / developerBaseUrl into devConnection (company typically CRONUS IS). Verified publish path on Cosmo Alpaca: multipart /dev/apps → HTTP 200.
Uninstall / unpublish: prefer Automation API (bc_dev_uninstall_app / bc_dev_unpublish_app). If those fail or the container only allows SSH ops, use cosmo_ssh_info then Uninstall-NavApp / Unpublish-NavApp over SSH.
bc_dev_run_tests: prefers Cosmo SSH (GET /Container/Ssh/{id}). SSH is usable when ipAddress and privateKey are present — do not require available===true (available=false is expected while Starting after Stop→Start). Connects as sshuser with privateKey (never logged). SSH recovery: the Cosmo SSH endpoint can drop out while the container keeps running (the container record has no ssh flag). Before a run bc_dev_run_tests (and bc_dev_build_runtime_package) therefore enable SSH and wait for it (ensureSsh, default on; sshWaitSeconds, default BC_DEV_SSH_ENSURE_WAIT_S or 300; allowRestart=true also permits Stop → Start). The result's sshEnsure lists what was done. Remote invoke is scp of local run-tests.ps1 (+ vendored PsTestFunctions.ps1 / ClientContext.ps1) → C:\Windows\Temp\…, then pwsh -NoProfile -File <remote> (fallback powershell.exe -File); best-effort remote delete afterward. Do not pipe the script on stdin to pwsh -Command - (Cosmo Windows OpenSSH aborts after the first Write-Host).
Cosmo SSH is inside the BC container (not a Docker host). Host-side Invoke-NavContainerTests / Run-TestsInBcContainer / Run-AlTests usually do not exist there. The remote script therefore:
Dot-sources
C:\Run\Prompt.ps1when present.If host helpers are missing → in-container Client Services path (same approach
Run-TestsInBcContaineruses inside the container): locate Service-folder Newtonsoft +Microsoft.Dynamics.Framework.UI.Client.dll, load PsTestFunctions (scp upload orInstall-Module BcContainerHelper),New-ClientContextto local NST/cs?tenant=…,Run-Tests(tries pages 130455 → 130202 → 130203 → 130409).Option B (BC 27.5+/28): if Client Services pages fail (page 130455 removed), best-effort
Invoke-NAVCodeunit130201 (CLI Test Runner / TestRunner-Internal) — documented clearly; JUnit may be absent.Soft diagnostics via
Get-NAVAppInfoif runners fail (toolkit is often already present on Cosmo; do not treat publish as a hard gate).
Brief SSH connect retries while Starting. When ip/key are missing, returns a blocked error (no silent fallback) with Stop→Start recreate + cosmo_create_container/sshEnabled=true hints. Cosmo OpenAPI has no /Container/Exec/{id}/… test-runner endpoint. mode=helper is local-docker only (containerName). Reuses stdio/devConnection NavUserPassword credentials. Returns structured counts + failure messages (truncated previews; never logs privateKey / Nav passwords beyond truncated previews).
Follow-up (out of scope here): alc compile orchestration.
Cosmo Alpaca (cosmo_*)
Same MCP server as bc_dev_*. Cosmo tools call the Alpaca API with a Bearer token (container lifecycle, feed deploy, SSH info, NST restart). App publish of arbitrary .app / CI artifacts stays on bc_dev_*.
Config (origo-bc-mcp)
Auth and backend resolve in order: tool arg → env → ~/.origo-bc-mcp/local.settings.json → built-in default. Prefer env for secrets; never commit tokens.
Setting | Env / file | Notes |
Bearer |
| Required for |
API base |
| Must be Alpaca API base, not the bare public host |
Default backendUrl (after this fix):
https://cosmo-alpaca-enterprise.westeurope.cloudapp.azure.com/api/alpaca/release
That matches Cosmo Alpaca VS Code 1.27 OpenAPI basePath {host}/api/alpaca/release. A bare host (https://cosmo-alpaca-enterprise.westeurope.cloudapp.azure.com) yields nginx 404 on POST /Container/Container/filter. Paths are joined as ${backendUrl}/Container/... — do not append /api/alpaca/release again if the caller already passes the full API base.
Example cosmo block in ~/.origo-bc-mcp/local.settings.json (prefer env for the token):
{
"cosmo": {
"backendUrl": "https://cosmo-alpaca-enterprise.westeurope.cloudapp.azure.com/api/alpaca/release",
"bearerToken": "env:COSMO_BEARER_TOKEN"
}
}Or only:
export COSMO_BEARER_TOKEN='…' # preferred over committing bearerToken
# optional override:
# export COSMO_BACKEND_URL='https://cosmo-alpaca-enterprise.westeurope.cloudapp.azure.com/api/alpaca/release'How to get a Bearer (VS Code 1.27): cosmo-alpaca.debugMode is not in the Settings UI. Add "cosmo-alpaca.debugMode": true to User settings.json, then Command Palette → Get GitHub API token or Get Azure DevOps API token (token copies to clipboard). Set COSMO_BEARER_TOKEN from that value.
Verify without echoing the secret: cosmo_whoami_config returns resolved backendUrl, whether a bearer is configured (+ length), and defaultBackendUrl / defaultPublicHost — never the token value.
Container tools (cosmo_*)
Tool | Cosmo API | What it is for |
|
| List/filter containers for the tenant |
|
| Status + derived REST/DEV URLs for |
| `POST /Container/Container[/gitHub | /azureDevOps |
|
| Start |
|
| Tear down ephemeral containers |
|
| Install from **NuGet |
|
| Installed app info blob from the container |
|
| SSH endpoint/credentials for NavApp fallback (read-only) |
|
| Makes SSH usable: enables it (or starts a stopped container with |
|
| Restart NST after stubborn publish/uninstall issues |
| (local) | Confirm backend + token configured (no secret echo) |
Public container host (for {id}rest / {id}dev) remains
https://cosmo-alpaca-enterprise.westeurope.cloudapp.azure.com — that is not the Alpaca API backendUrl.
Cosmo vs bc_dev_* boundary
Concern | Use |
Container CRUD, Start/Stop, SSH info, NST restart |
|
Install app from NuGet / Azure DevOps feed |
|
Publish local |
|
Uninstall / unpublish |
|
AL unit tests |
|
Ephemeral Cosmo loop (policy)
cosmo_create_container(CreateBcContainer / CreateGitHubBcContainer / Azure DevOps / standalone)cosmo_get_container→ wire derived…/{id}rest+…/{id}devintodevConnection(CRONUS IS)bc_dev_publish_*/ tests /bc_dev_uninstall_app(Automation API)cosmo_delete_container
Do not treat standing personal bc28-is or machine ORI1058 as the long-term test host (avoid polluting personal containers). bc28-is-grok is optional interim only. Cosmo lifecycle tools live in this same MCP server alongside bc_dev_*.
Status & continuation
Scaffold + dual auth + tenant access guard + discovery tools are in place and
compile/run. Next: migrate the ~40+ BC tools from the legacy server (api/mcp/tools/*)
into src/tools/, then deploy to dev via Azure DevOps.
Further notes live in the docs/ folder of the source repository on Azure DevOps
(BC-PTE-CloudEvents → Cloud Events MCP). They are not part of this published
package:
docs/PROJECT-STATUS.md— full state, decisions, tool-migration inventory, open questions, resume checklist.docs/RESUME-PROMPT.md— ready-to-paste prompt to continue the work later.docs/local-dev.md— how to start the server locally (Basic auth, on-prem/SaaS).docs/devops-setup.md— cross-tenant deploy setup.
Local install
Run with Docker
The included Dockerfile builds a production image with PM2 for automatic restarts. It sets MCP_HOST=0.0.0.0 so published ports work; the image is still intended for private hosts only (not public Caddy). Configuration is stored in a /data volume inside the container and managed through the web dashboard.
Step 1: Build the image
docker build -t origo-bc-mcp https://github.com/businesscentralal/origo-bc-mcp.gitStep 2: Run the container
Mount a local folder for persistent config storage:
docker run -d --name origo-bc-mcp --restart unless-stopped -p 3000:3000 -v "E:\Docker Storage\origo-bc-mcp:/data" -e MCP_ENCRYPTION_KEY=<64-hex-chars> -e MCP_ADMIN_USER=admin -e MCP_ADMIN_PASSWORD=<your-password> -e OLLAMA_PROXY_TARGET=http://<ollama-host>:11434 origo-bc-mcpdocker run -d --name origo-bc-mcp --restart unless-stopped -p 3000:3000 -v /path/to/origo-bc-mcp-data:/data -e MCP_ENCRYPTION_KEY=<64-hex-chars> -e MCP_ADMIN_USER=admin -e MCP_ADMIN_PASSWORD=<your-password> -e OLLAMA_PROXY_TARGET=http://<ollama-host>:11434 origo-bc-mcp
MCP_ENCRYPTION_KEYencrypts connection secrets (passwords, client secrets) at rest in the volume. Generate one with:openssl rand -hex 32
MCP_ADMIN_USER/MCP_ADMIN_PASSWORDsecure the dashboard on first boot. Without these, the dashboard is open until you configure Basic Auth in the setup UI.
Step 3: Configure via the dashboard
Open http://localhost:3000/dashboard/setup in your browser.
On first launch (no config exists), the dashboard is open. Add your first connection and enable Basic Auth — subsequent visits will require login.
The setup page lets you:
Add SaaS (Entra) or On-Premises BC connections
Validate connections (test button confirms access and lists companies)
Configure Basic Auth credentials (used for both MCP access and dashboard login)
Remove connections
Step 4: Connect your MCP client
Point your MCP client (VS Code Copilot, Claude Desktop, Open WebUI, etc.) at:
http://localhost:3000/mcpWith Basic Auth header using the credentials you configured in the dashboard.
Dashboard login
The dashboard is protected by the same Basic Auth credentials configured in Setup. If you haven't configured Basic Auth yet, the dashboard is open (to allow first-time setup).
Environment variables
Variable | Default | Description |
|
| Directory for |
| — | Bootstrap admin username (sets Basic Auth on first start if no config exists) |
| — | Bootstrap admin password (pair with |
| — | 64 hex characters for AES-256-GCM encryption of secrets at rest |
|
| Public URL for the server |
|
| Listen port |
| — | Set to |
|
| Ollama server URL for the |
Generating MCP_ENCRYPTION_KEY
The key must be exactly 64 hex characters (32 bytes). Generate one with any of these:
# OpenSSL (Linux/macOS/Git Bash)
openssl rand -hex 32
# Node.js (any platform)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# PowerShell (Windows)
-join ((1..32) | ForEach-Object { '{0:x2}' -f (Get-Random -Max 256) })Store the key securely — if you lose it, secrets encrypted with it cannot be recovered.
Docker Compose example
services:
mcp:
build: https://github.com/businesscentralal/origo-bc-mcp.git
ports:
- "3000:3000"
volumes:
- ./mcp-data:/data
environment:
- MCP_ENCRYPTION_KEY=${MCP_ENCRYPTION_KEY}
- MCP_ADMIN_USER=${MCP_ADMIN_USER:-admin}
- MCP_ADMIN_PASSWORD=${MCP_ADMIN_PASSWORD}
restart: unless-stoppedHealth check
curl http://localhost:3000/healthzInstall from tarball
The server is also published to the Azure Artifacts feed BC-PTE-CloudEvents for
local dev/test on Windows and macOS. Installing from GitHub
(Part A.2) is the simpler route and is what these
instructions assume.
If you install from the feed or from a downloaded tarball instead, the setup
guides live in the docs/ folder of the source repository on Azure DevOps
(docs/setup-from-tarball.md, docs/setup-macos.md, docs/setup-windows.md).
Everything from Part B onwards is identical either way.
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server unifying ERPs, CRMs, APIs and knowledge base for Claude, ChatGPT and Gemini.
Model Context Protocol server for the Apideck Unified API. Connect any MCP-compatible agent framework to 100+ accounting systems, HRIS platforms, file storage providers, and more through one integration. More information https://www.apideck.com/mcp-server
Connect any AI assistant to Odoo 16–19 via OAuth 2.0 + PKCE. 400 free calls, no local install.
Connect any AI agent to 1,000+ apps and 27,000+ actions through one remote MCP server (OAuth).
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI clients to interact with Microsoft Dynamics 365 Business Central through MCP, providing access to customers, items, sales orders, and allowing creation of new records via natural language.2-
- AlicenseAqualityAmaintenanceGive AI assistants direct access to Microsoft Dynamics 365 Business Central via native WebSocket protocol.1446 npm41MIT
- AlicenseAqualityCmaintenanceModel Context Protocol (MCP) server for Microsoft Dynamics 365 Business Central. Provides AI assistants with direct access to Business Central data through properly formatted API v2.0 calls.622 npm8MIT
- AlicenseAqualityCmaintenanceEnables AI assistants to directly access and interact with Microsoft Dynamics 365 Business Central using native WebSocket protocol.1446 npmMIT