Skip to content

Releases: SShadowS/business-central-mcp

v1.7.0

Choose a tag to compare

@github-actions github-actions released this 01 Sep 14:34

Added

  • Central connection config. A ~/.bc-mcp/config.jsonc (or BC_MCP_CONFIG
    / <cwd>/.bc-mcp.jsonc) file defines named connections, an optional
    default, and an optional map[] from repo path to connection, so one
    globally-registered server can serve multiple BC instances without a
    BC_* env block per repo. Resolution order per field: an explicit BC_*
    env var, then BC_CONNECTION=<name>, then a map[] path match, then
    default. Secrets can be kept out of the file with ${ENV} references.
    A <cwd>/.env (or BC_ENV_FILE) is also auto-loaded at startup
    (override:false — real environment variables still win). With no config
    file and no .env present, the server behaves exactly as before.

Full Changelog: v1.6.0...v1.7.0

v1.6.0

Choose a tag to compare

@github-actions github-actions released this 17 Aug 12:13

BC Online (SaaS) support: the server now runs against
businesscentral.dynamics.com as well as on-prem, with a password-free
sign-in for the UI tools and device-code OAuth for bc_query. On-prem
NavUserPassword is unchanged.

Added

  • SaaS /csh web-client session (ESTS cookie). A portal URL
    https://businesscentral.dynamics.com/{aadTenant}/{environment} selects
    authMode: SaasWeb. The first UI tool opens a local 127.0.0.1 sign-in
    window (password and MFA stay there — never in env, never in chat); portal
    cookies persist under STATE_DIR/saas-web-cookies.json (mode 0600, per
    repo) and are reused across process restarts. The /csh WebSocket is
    discovered on the cluster host after sign-in — no cluster URL in config.
  • OAuth / Microsoft Entra ID authentication for bc_query. A SaaS portal
    URL (or BC_AUTH=OAuth) acquires a Standard API token via device-code and
    uses it as Authorization: Bearer for bc_query. Refresh tokens are cached
    under STATE_DIR (mode 0600). BC_USERNAME / BC_PASSWORD are not required
    in this mode.
  • bc_query no longer opens a /csh session. The OData tool is
    independent of the web-client WebSocket, so SaaS OAuth works for bulk reads
    even when the first-party web-client cookie session cannot be established.
  • Device-code sign-in surfaces in chat. When bc_query needs a sign-in it
    fails fast with DEVICE_LOGIN_REQUIRED whose message carries the
    https://microsoft.com/devicelogin URL and user code (instead of blocking the
    tool call and printing the code on stderr, which MCP clients never show).
    The pending sign-in is persisted in STATE_DIR/oauth-pending.json; retrying
    the tool polls once and resumes — same code, no re-prompt — then runs the
    query once sign-in is complete. BC_CLIENT_ID is required: a
    publisher-owned multi-tenant public app (see README "bc_query on SaaS") —
    customer tenants never register anything. Borrowed Microsoft first-party
    clients fail at sign-in with AADSTS65002 on hardened tenants.
  • SaaS URL parsing. A portal URL such as
    https://businesscentral.dynamics.com/7bcb54ae-…/DEV is split into Entra
    tenant + environment. OData is derived as
    https://api.businesscentral.dynamics.com/v2.0/{tenant}/{environment}.
    A verified AAD domain (contoso.onmicrosoft.com) is accepted in the tenant
    segment as well as a GUID.
  • npx business-central-mcp login primes the SaaS cookie file ahead of
    the first tool call.

Changed

  • Config: new BC_AUTH, BC_AAD_TENANT_ID, BC_ENVIRONMENT, BC_OAUTH_SCOPE,
    BC_CLIENT_ID. BC_USERNAME / BC_PASSWORD are required only for
    NavUserPassword; a SaaS password in env is ignored.
  • Session-lifecycle hardening. A dead or revoked SaaS session is detected
    and reopens interactive sign-in via a windowed, episode-based escalation
    (transient outages never destroy valid cookies; a brief portal interstitial
    during backoff never escalates; a genuinely dead session does). A fresh
    sign-in is verified behaviorally — signing in with the wrong Microsoft
    account or tenant fails with a clear, non-retryable error and saves nothing,
    instead of silently degrading into repeat prompts. A session revoked while
    the cluster tab is bound now recovers instead of wedging.
  • HTTP route-gating. Unknown REST paths 404 and non-POST /mcp 405
    before any BC session is created, so a stray request (a LAN scanner's
    GET /favicon.ico) can no longer trigger session creation or pop the
    interactive SaaS sign-in window. Sign-in-flow errors on the REST path now
    carry their code and payload (verification URL, user code) instead of an
    opaque 500.

Fixed

  • bc_query OData errors preserve their typed BC error (code + hint) instead
    of collapsing a DEVICE_LOGIN_REQUIRED into a generic network error.
  • Cookie handling: Set-Cookie deletions (Max-Age=0 / past Expires) are
    honored per RFC 6265; cookies scoped to a bare public suffix are rejected;
    the portal auth cookie is matched by resolved-GUID suffix so domain-form
    tenants work; the SaaS cluster host is allow-listed to dynamics.com.
  • OAuth token refresh is single-flighted, so concurrent bc_query calls at
    token expiry share one grant instead of racing and wiping the cache; a
    captive-portal non-JSON 200 during device-code polling no longer churns
    the pending code.

What's Changed

  • docs: upgrade VSCode install badge to prompt for credentials via inputs by @SShadowS in #6
  • docs: document BC_APPLICATION_ID=NAV for on-prem containers by @SShadowS in #11
  • feat(saas): BC Online /csh web-client session (ESTS) by @FBakkensen in #12
  • fix(saas): PR #12 review follow-ups (#11-16 hardening/cleanup) by @SShadowS in #13
  • feat(saas): session-lifecycle hardening — escalation, sign-in verification, warm-path recovery by @FBakkensen in #15
  • docs: correct the bc_query SaaS error-path description by @SShadowS in #16

New Contributors

Full Changelog: v1.5.0...v1.6.0

v1.5.0

Choose a tag to compare

@SShadowS SShadowS released this 25 Jul 12:30

Added

  • Multi-row selection. bc_execute_action accepts bookmarks: string[] to
    select N rows and invoke a selection-consuming action (Delete) over the whole
    set atomically (SetCurrentRowAndRowsSelection + InvokeAction in one queue
    entry). The anchor is bookmarks[0] and must be a member of the set. Only
    selection-consuming actions act on all rows; Edit/View/DrillDown/New are
    current-row-only and are rejected with bookmarks[]. A stale anchor returns
    INVALID_BOOKMARK; BC_MAX_SELECTION (default 100) caps the set.
  • Generic file download capture. bc_execute_action, bc_respond_dialog,
    bc_wizard_navigate, and bc_run_report now return downloads: Download[]
    (inline base64 + optional disk write) and externalUris[] via a shared
    DownloadService. Only same-origin URLs under an allowlisted BC file path are
    fetched (SSRF/credential-leak guard); external and mailto: URIs are surfaced
    but never dereferenced. Per-file/aggregate/count caps and BC_DOWNLOAD_DIR
    are configurable.
  • Config. BC_MAX_SELECTION; download limits BC_MAX_DOWNLOAD_BYTES,
    BC_MAX_DOWNLOAD_TOTAL_BYTES, BC_MAX_DOWNLOADS, BC_DOWNLOAD_DIR
    (falls back to BC_REPORT_DIR).

Changed

  • BREAKING (bc_run_report): the singular download field is replaced by
    downloads: Download[] for parity with the other download-capturing tools.
  • Default client version / serverMajor now default to BC28.

Fixed

  • BC 28.3 /csh 403. The WebSocket upgrade now sends an Origin header, so
    BC 28.3's RequestOriginValidationMiddleware no longer rejects the connection.
    Same-origin only; a no-op on BC 28.0.
  • Multi-row action silent no-op. A multi-row Delete on a page that forbids it
    (e.g. the Customer list) previously returned success with nothing deleted. BC
    disables such actions server-side (Enabled=false); bc_execute_action now
    detects that and returns MULTI_ROW_ACTION_UNAVAILABLE instead of a lying
    success. Where BC keeps the action enabled it deletes all selected rows.
  • Download disk-write filenames are sanitized against path traversal.

Full Changelog: v1.4.0...v1.5.0

v1.4.0

Choose a tag to compare

@github-actions github-actions released this 11 Jul 19:49

Full Changelog: v1.3.0...v1.4.0

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 10 Jul 17:11

Full Changelog: v1.1.0...v1.3.0

v1.1.0

Choose a tag to compare

@github-actions github-actions released this 16 Jun 11:42

Full Changelog: v1.0.2...v1.1.0

v1.0.2

Choose a tag to compare

@github-actions github-actions released this 30 Apr 22:26