Skip to main content
Glama
SShadowS

business-central-mcp

by SShadowS

Overview

Property

Value

Language

TypeScript / Node 20+

npm package

business-central-mcp

BC versions

BC27, BC28 (wire-compatible)

Auth

On-prem NavUserPassword. BC Online: ESTS cookie session for /csh (no password in env) + device-code for bc_query.

Tools

12

Tests

901 unit/protocol + 111 integration

License

MIT

Related MCP server: origo-bc-mcp-server

Install

BC Online (sandbox / production): do not put a password in env. Copy the portal URL from your browser and follow SaaS sandbox setup. The snippets below are for on-prem NavUserPassword.

VSCode

Install in VSCode

Click the badge. VSCode opens and prompts for your BC URL, username, and password (on-prem), then writes the configured entry to your user mcp.json. For BC Online, skip the badge and use the SaaS sandbox env (URL + optional email only).

Workspace: create .vscode/mcp.json:

{
  "servers": {
    "business-central": {
      "command": "npx",
      "args": ["-y", "business-central-mcp"],
      "env": {
        "BC_BASE_URL": "http://your-bc-server/BC",
        "BC_USERNAME": "your-user",
        "BC_PASSWORD": "your-password"
      }
    }
  }
}

BC Online — same file, no password:

{
  "servers": {
    "business-central": {
      "command": "npx",
      "args": ["-y", "business-central-mcp"],
      "env": {
        "BC_BASE_URL": "https://businesscentral.dynamics.com/<aad-tenant-id>/DEV",
        "BC_USERNAME": "you@tenant.com"
      }
    }
  }
}

Claude Code

claude mcp add business-central \
  -e BC_BASE_URL=http://your-bc-server/BC \
  -e BC_USERNAME=you \
  -e BC_PASSWORD=secret \
  -- npx -y business-central-mcp

Scope it to the current project with --scope project. See claude mcp --help for scoping options.

BC Online (no password):

claude mcp add business-central \
  -e BC_BASE_URL=https://businesscentral.dynamics.com/<aad-tenant-id>/DEV \
  -e BC_USERNAME=you@tenant.com \
  --scope project \
  -- npx -y business-central-mcp

Claude Desktop

  1. Download the latest .dxt from Releases.

  2. Double-click. Claude Desktop opens Settings → Extensions and prompts for BC URL, username, and password (on-prem). For BC Online, use the manual snippet instead — do not store a SaaS password.

  3. Restart Claude Desktop.

Edit claude_desktop_config.json:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

  • Linux: ~/.config/Claude/claude_desktop_config.json

{
  "mcpServers": {
    "business-central": {
      "command": "npx",
      "args": ["-y", "business-central-mcp"],
      "env": {
        "BC_BASE_URL": "http://your-bc-server/BC",
        "BC_USERNAME": "your-user",
        "BC_PASSWORD": "your-password"
      }
    }
  }
}

Restart Claude Desktop.

Configuration

Variable

Required

Default

Description

BC_BASE_URL

Yes

BC server base URL, e.g. http://your-bc-server/BC, or a SaaS portal URL https://businesscentral.dynamics.com/{aadTenant}/{environment}

BC_USERNAME

NavUserPassword

On-prem username. On SaaS this is only an email prefill for the local sign-in window.

BC_PASSWORD

NavUserPassword

On-prem password. Ignored on SaaS (never put a SaaS password in env).

BC_AUTH

No

auto

auto (SaaS URL → SaasWeb, otherwise NavUserPassword), OAuth, SaasWeb, or NavUserPassword

BC_AAD_TENANT_ID

OAuth (if not in URL)

Entra tenant GUID. Taken from a SaaS BC_BASE_URL when present

BC_ENVIRONMENT

No

from URL

SaaS environment name (DEV, sandbox, production)

BC_CLIENT_ID

bc_query on SaaS

Multi-tenant public Entra app for device-code sign-in (see bc_query on SaaS). UI tools do not need it

BC_OAUTH_SCOPE

No

user_impersonation + offline_access

Override the Entra scope for bc_query device-code

BC_PROFILE

No

server default

Profile id, e.g. BUSINESS MANAGER. Affects which Role Center loads and which pages Tell Me indexes.

BC_TENANT_ID

No

default

On-prem multi-tenant id. SaaS uses the Entra tenant from the URL.

BC_CLIENT_VERSION

No

27.0.0.0

Version reported to BC during session open.

BC_APPLICATION_ID

No

FIN

navigationContext.applicationId sent at session open. SaaS and cronus images expect FIN; some on-prem containers expect NAV (see below).

PORT

No

3000

HTTP transport port (stdio transport ignores this).

LOG_LEVEL

No

info

debug / info / warn / error.

LOG_DIR

No

./logs

Directory for log files.

STATE_DIR

No

{cwd}/.state

Per-repo directory for saas-web-cookies.json and oauth-tokens.json (mode 0600). Relative paths resolve against the MCP process working directory (the project you started the agent in). Sessions in the same repo share the file; different repos never share a login.

BC_INVOKE_TIMEOUT

No

30000

Per-invoke timeout in ms. Kills hung sessions.

BC_RECONNECT_MAX_RETRIES

No

4

Reconnect attempts after session death.

BC_RECONNECT_BASE_DELAY

No

1000

Base delay (ms) for exponential reconnect backoff.

Central connection config

Running several Claude Code sessions against several BC instances no longer requires a full BC_* env block in every repo's .mcp.json. Register the server once at user scope and define the connections in one file.

  1. Register the server globally:

    claude mcp add business-central -s user -- node U:/git/bc-mcp/node_modules/tsx/dist/cli.mjs U:/git/bc-mcp/src/stdio-server.ts
  2. Create ~/.bc-mcp/config.jsonc (see config.jsonc.example): a set of named connections, an optional default, and an optional map[] from repo path to connection.

  3. Each session picks its connection, highest priority first:

    • an explicit BC_* env var (e.g. BC_BASE_URL) always wins for that field;

    • BC_CONNECTION=<name> selects a named connection;

    • a map[] entry whose path matches the session's working directory;

    • the default connection.

Keep secrets out of the file with ${ENV} references (expanded from the process environment); on macOS/Linux the file should be mode 0600. SaaS connections carry no password — sign in via the local window or npx business-central-mcp login. With no config file present, the server runs exactly as before from plain BC_* environment variables.

A <cwd>/.env (or the file at BC_ENV_FILE) is also auto-loaded at startup, before connection resolution — real environment variables set outside the file still win (override:false).

On-prem containers: set BC_APPLICATION_ID=NAV

If sign-in and the WebSocket upgrade both succeed but the session dies at OpenSession with NavCancelCredentialPromptException, the server is rejecting the default applicationId (FIN). On-prem BcContainerHelper containers (the onprem artifact type) generally expect NAV:

BC_APPLICATION_ID=NAV

The failure is misleading because authentication and the /csh upgrade complete first (you get a 101); BC only rejects the applicationId inside the OpenSession RPC body. SaaS and cronus images keep the FIN default. Verified against BC 27.1 onprem (see issue #10).

SaaS sandbox setup

You only need the URL from the browser address bar — the same one you use to open Business Central Online:

https://businesscentral.dynamics.com/<aad-tenant-id>/<environment>

<environment> is usually DEV, sandbox, or production. Do not set BC_PASSWORD. Company policy and this server both treat a SaaS password in env as wrong.

  1. Copy that portal URL into BC_BASE_URL (no extra path, no query string).

  2. Optionally set BC_USERNAME to your work email — that only prefills the sign-in form.

  3. Point the MCP at this project (stdio, Grok .grok/config.toml, Claude --scope project, or a workspace mcp.json). Leave STATE_DIR unset so cookies land in {project}/.state/.

  4. Start the agent on a machine with a display (Linux needs DISPLAY or WAYLAND_DISPLAY). Headless CI cannot complete MFA.

  5. Ask the agent to open a page (bc_open_page, e.g. Customer List = 22). A local window (127.0.0.1) opens. Sign in with Microsoft and complete Authenticator there. Do not paste the password into chat or tool arguments.

  6. Retry the tool. Cookies are saved as {project}/.state/saas-web-cookies.json (mode 0600). Later sessions in the same repo reuse them; another repo needs its own sign-in.

Human shortcut (same working directory as the MCP):

npx business-central-mcp login
# from a source checkout:
npx tsx src/stdio-server.ts login

Grok (project-scoped, no password):

# .grok/config.toml  — not committed if it holds a tenant URL you do not want shared
[mcp_servers.business-central]
command = "npx"
args = ["-y", "business-central-mcp"]

[mcp_servers.business-central.env]
BC_BASE_URL = "https://businesscentral.dynamics.com/<aad-tenant-id>/DEV"
BC_USERNAME = "you@tenant.com"

From a source checkout, point command / args at node + node_modules/tsx/dist/cli.mjs + src/stdio-server.ts instead of npx.

Claude Desktop / VS Code (no BC_PASSWORD):

{
  "mcpServers": {
    "business-central": {
      "command": "npx",
      "args": ["-y", "business-central-mcp"],
      "env": {
        "BC_BASE_URL": "https://businesscentral.dynamics.com/<aad-tenant-id>/DEV",
        "BC_USERNAME": "you@tenant.com"
      }
    }
  }
}

The WebSocket is not on the portal host. After sign-in the server discovers the cluster and uses Origin: https://businesscentral.dynamics.com. You never put a cluster URL in config.

bc_query (OData) on SaaS

bc_query does not use the /csh cookie session. When sign-in is needed the first call returns DEVICE_LOGIN_REQUIRED with a https://microsoft.com/devicelogin URL and user code — complete it in a browser and retry; the retry picks up the pending sign-in and runs the query. The refresh token is stored in STATE_DIR/oauth-tokens.json (mode 0600).

bc_query talks to https://api.businesscentral.dynamics.com/v2.0/{tenant}/{environment}/api/v2.0 with the Bearer token. If BC_CLIENT_ID is not configured it returns OAUTH_NOT_CONFIGURED (device-code that has not been completed returns DEVICE_LOGIN_REQUIRED, above); it never sends Basic.

Which client id signs in (BC_CLIENT_ID)

BC_CLIENT_ID is required for bc_query on BC Online: a multi-tenant public Entra app with delegated Dynamics 365 Business Central / user_impersonation. The publisher registers it ONE time in their own tenant; customer tenants register nothing — each user consents at first sign-in (user_impersonation is user-consentable), and tenants that disable user consent need a one-time admin-consent click.

Do not borrow a Microsoft first-party client (Azure PowerShell 1950a258-… as New-BcAuthContext does, Azure CLI, …): on tenants with Entra first-party hardening the sign-in fails in the browser with AADSTS65002 ("consent between first party application and first party resource must be configured via preauthorization"), which no tenant admin can consent around. Verified live 2026-08-16 — the same sign-in succeeds on one tenant and fails with 65002 on another. A third-party multi-tenant app is structurally immune (65002 only gates Microsoft-owned client/resource pairs).

Create the app (once, in the publisher tenant):

az ad app create --display-name "business-central-mcp" \
  --is-fallback-public-client true \
  --sign-in-audience AzureADMultipleOrgs \
  --required-resource-accesses '[{"resourceAppId":"996def3d-b36c-4153-8607-a6fd3c01b89f","resourceAccess":[{"id":"bce0976a-cb0b-473b-8800-84eda9f8e447","type":"Scope"}]}]' \
  --query appId -o tsv

(996def3d… is the Dynamics 365 Business Central resource; bce0976a… is its delegated user_impersonation scope.) Put the printed appId in BC_CLIENT_ID.

Known wart: when the browser sign-in fails (65002, blocked consent), Entra keeps the device code authorization_pending, so retries re-serve the same doomed code until it expires (~15 min). Fix the client id / consent, wait out or ignore the old code, and retry for a fresh one.

What can it do?

Tool

What it does

bc_open_page

Open any page by ID -- lists, cards, documents, role centers. Returns the page as sections[] with header, lines, factboxes, and Role Center cuegroup tiles.

bc_read_data

Refresh a single section: filter, paginate, slice, project tab/columns. Returns the same Section shape as bc_open_page.

bc_write_data

Write field values; BC validates and echoes confirmed values. Section-aware (lines, factboxes, header).

bc_execute_action

Run header / row / wizard actions, OR drill down on Role Center cue tiles via cue input.

bc_respond_dialog

Handle confirmation prompts and request pages

bc_navigate

Select rows, drill down into records, field lookups

bc_search_pages

Tell Me search. Returns { name, objectType, runTarget, departmentPath, category, score } per result.

bc_close_page

Close a page and free server resources

bc_switch_company

Switch to a different company mid-session

bc_list_companies

Discover available companies

bc_run_report

Execute reports and fill request page parameters

bc_wizard_navigate

Drive NavigatePage / wizard flows (back / next / finish / cancel)

How it works

This server speaks BC's internal WebSocket protocol directly -- the same protocol the browser-based web client uses. It was reverse-engineered from decompiled BC server assemblies. No OData endpoints, no SOAP services, no Selenium.

One WebSocket connection per session. All operations serialized through a promise queue. BC27 and BC28 are wire-compatible.

LLM (Claude / Copilot / etc.)
   |
   v   MCP (stdio or HTTP)
business-central-mcp
   |
   v   WebSocket + JSON-RPC
BC Web Service Tier (BC27 / BC28)
   |
   v   internal calls
BC Server

bc_open_page returns the page as a flat list of sections:

{
  "pageContextId": "session:page:21:abc",
  "pageType": "Card",
  "caption": "Customer Card",
  "isModal": false,
  "sections": [
    { "sectionId": "header",                       "kind": "header",  "fields": [...], "actions": [...] },
    { "sectionId": "factbox:Customer Statistics",  "kind": "factbox", "fields": [...] }
  ]
}

Each section carries its own content shape:

  • Card-style (header on Card pages, factbox, requestPage): fields[] and (for header) actions[]

  • List-style (lines on Documents, header on List pages, repeater subpages): rows[] and totalRowCount

  • Cue tiles (Role Center hosted CardParts): cues[] with each tile's name, value, groupCaption, synopsis, hasAction. Drill down with bc_execute_action { section, cue }.

bc_read_data returns a single Section for the requested sectionId (defaults to "header"). The section ID for a FactBox or subpage comes from the bc_open_page response.

  • Automatic reconnect with exponential backoff after session death

  • Handles BC's ~15s NTLM auth slot hold after crashes

  • Auto-dismisses license popups on fresh databases

  • Invoke timeout kills hung sessions and triggers recovery

  • Auto-recovery from LogicalModalityViolationException mid-session: reconciles the modal stack and retries transparently; falls back to session reset when BC keeps a confirm dialog sticky

Key files

File

Purpose

src/stdio-server.ts

npm bin entry -- stdio MCP transport

src/server.ts

HTTP MCP transport entry

src/mcp/

MCP tool registry, schemas, request handler

src/operations/

One handler per tool (bc_open_page, bc_read_data, etc.)

src/services/

Page, data, action, navigation, search business logic

src/protocol/

WebSocket transport, wire types, captures

src/session/

Session lifecycle, modal stack, reconnect

manifest.json

Claude Desktop Extension manifest

scripts/build-dxt.ts

Builds .dxt artifact for Claude Desktop

.github/workflows/release.yml

Builds + attaches .dxt on v* tag pushes

ROADMAP.md

Deferred work (Cursor, init wizard)

Development

git clone https://github.com/SShadowS/business-central-mcp
cd business-central-mcp
npm install
npm run start:stdio-direct   # Run from source
npm test                     # unit + protocol tests
npm run test:integration     # Cronus28 integration tests (requires running BC server)
npm run test:saas            # BC Online smoke (needs a signed-in STATE_DIR cookie file)

Roadmap

Cursor support, an interactive init wizard, and a few protocol gaps. See ROADMAP.md for the full list and priorities.


Author: Torben Leth (sshadows@sshadows.dk) License: MIT (see LICENSE)

Available Tools

14 tools
bc_close_pageA

Closes an open Business Central page and frees its server-side resources including the WebSocket form session. Always call this when you are finished working with a page to prevent resource leaks on the BC server. Requires a pageContextId from bc_open_page.

After closing, the pageContextId becomes invalid -- any subsequent bc_read_data, bc_write_data, bc_execute_action, or bc_navigate calls using it will fail. It is safe to call this even if prior operations on the page encountered errors. If you opened a drill-down page via bc_navigate (which returns a new pageContextId), close both the drill-down page and the original list page when done.

Do NOT call this in the middle of a multi-step workflow -- finish all reads, writes, and actions on the page first. Do NOT call this to "reset" a page; use bc_read_data to refresh data instead.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageContextIdYesPage context ID returned by bc_open_page. Becomes invalid after closing.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so the description carries the full burden. It thoroughly explains that the pageContextId becomes invalid, subsequent calls will fail, it is safe to call after errors, and when to close drill-down pages.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise, front-loaded, and every sentence adds value. It could be slightly more terse but is well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations, no output schema, and one parameter, the description is very complete, covering purpose, usage, behavior, and pitfalls.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with one parameter already described well. The description adds minimal extra meaning (e.g., confirms it's from bc_open_page), so baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool closes an open Business Central page and frees server resources, distinguishing it from siblings like bc_read_data or bc_navigate.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to call (after finishing work), when not to call (mid-workflow or to reset), and alternatives (use bc_read_data to refresh). It also covers special cases like drill-down pages.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_execute_actionA

Executes either a named action OR a cue-tile drill-down on an open page. Pass action for header / line / system actions (Post, Delete, New, Release). Pass cue for Role Center cue tiles to open the underlying list (e.g. cue: "Sales Quotes" with section: "subpage:Activities" opens the Sales Quotes list). Requires a pageContextId from bc_open_page.

For cue drill-down, also pass section pointing at the subpage that owns the cuegroup. The returned openedPages array contains the targetPageContextId of the newly-opened list page.

For a named action: validates the action is enabled, sends the InvokeAction RPC, applies the resulting events, and returns updatedFields / changedSections / dialogsOpened / openedPages.

Use exactly one of "action" or "cue" -- passing both is an error.

If the action triggers a confirmation dialog or modal page, the response includes a dialogsOpened array with the dialog's formId and details. When requiresDialogResponse is true, you must follow up with bc_respond_dialog to confirm or cancel.

Row-scoped actions (Delete, Edit on a list row) require targeting a specific row. Use rowIndex (0-based) or bookmark to specify which row the action applies to. For Document pages, use section to disambiguate between header and line actions (e.g., "Delete" on header deletes the whole document, "Delete" on "lines" deletes one line).

For batch operations across multiple rows (e.g. deleting several lines at once), pass bookmarks (an array) instead of bookmark/rowIndex -- the first entry is the anchor row. Only actions that consume a selection (e.g. Delete) act on all listed rows; current-row-only actions (Edit, View, DrillDown, New) reject bookmarks[] and must use bookmark/rowIndex instead. bookmarks[] is mutually exclusive with bookmark, rowIndex, and cue.

Pass expectedStateVersion (from a prior bc_read_data or bc_open_page stateVersion field) to guard against acting on drifted state. If the page has been mutated by async events or a sibling operation since that read, the call is immediately rejected with code STALE_CONTEXT before touching BC. Re-read with bc_read_data to get the current stateVersion, then retry. Omit expectedStateVersion to skip the check.

Do NOT use this for writing field values -- use bc_write_data. Do NOT use this to open records from a list -- use bc_navigate with drill_down action instead.

Examples:

  • Drill into a cue tile: { "pageContextId": "rc1", "section": "subpage:Activities", "cue": "Sales Quotes" }

  • Post a sales order: { "pageContextId": "so1", "action": "Post" }

  • Delete a row: { "pageContextId": "list1", "action": "Delete", "bookmark": "..." }

  • Create new record: { "pageContextId": "abc", "action": "New" }

  • Delete a document line: { "pageContextId": "abc", "action": "Delete", "section": "lines", "rowIndex": 2 }

  • Delete multiple rows in one call: { "pageContextId": "list1", "action": "Delete", "bookmarks": ["bk1", "bk2"] } -- only selection-consuming actions like Delete act on all listed rows

  • Execute with staleness guard: { "pageContextId": "abc", "action": "Post", "expectedStateVersion": 5 }

If the action produces a file (Open in Excel, Print, export), its bytes appear in downloads[]; links BC would open externally appear in externalUris[] and are never fetched by the server.

ParametersJSON Schema
NameRequiredDescriptionDefault
cueNoCue tile name to drill down on (e.g. "Sales Quotes", "Pending Approvals"). Use with section pointing at the subpage that owns the cuegroup. Use action OR cue, not both.
actionNoAction caption name to execute (case-insensitive). Use action OR cue, not both. Must match a visible, enabled action from bc_open_page response.
sectionNoSection context. Required when using cue; optional for action. Examples: "lines", "subpage:Activities".
bookmarkNoStable row identifier for row-scoped actions.
rowIndexNo0-based row position for row-scoped actions.
bookmarksNoStable row identifiers for a MULTI-ROW action (batch delete, apply-entries). The first bookmark is the anchor/current row. Mutually exclusive with bookmark, rowIndex, and cue. Bookmarks must come from a bc_read_data of the same section and must still be loaded. Only actions that consume a selection (e.g. Delete) act on all rows; Edit/View/DrillDown/New use the anchor only and are rejected with bookmarks[].
pageContextIdYesPage context ID returned by bc_open_page.
expectedStateVersionNoOpt-in staleness guard. Pass the stateVersion from a prior bc_read_data or bc_open_page response. If the page state has changed since that read (async events or sibling writes mutated it), the call is rejected immediately with code STALE_CONTEXT before touching BC. Omit to skip the check.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Despite no annotations, the description fully discloses behavior: validates action, sends RPC, applies events, returns various response fields, handles batch operations, staleness guard, file downloads, and dialog requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections and examples, but somewhat lengthy. It earns its length with valuable information, though could be slightly more concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers all necessary aspects given 8 parameters and no output schema: prerequisites, mutually exclusive fields, batch behavior, dialog handling, staleness guard, and file outputs.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, yet description adds substantial context beyond schema definitions: examples for cue, section, bookmarks; explains mutual exclusivity; describes staleness guard behavior for expectedStateVersion.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states it executes a named action or cue-tile drill-down on an open page, using strong verbs and specific resource references. It distinguishes from siblings like bc_write_data and bc_navigate.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides clear when-to-use guidance, including explicit exclusions for writing fields (use bc_write_data) and navigating (use bc_navigate). Also explains when to follow up with bc_respond_dialog.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_list_companiesA

List all companies available in the current Business Central environment. Returns an array of company names along with the currently active company name. Use this before bc_switch_company to verify the target company exists and to discover available companies.

This tool opens the BC Companies system page internally, reads all entries, and closes it. It does not affect your currently open pages or session state. No parameters are required.

Do NOT use this if you already know the company name -- call bc_switch_company directly. If you need to work with data in a specific company, use bc_switch_company followed by bc_open_page.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Despite no annotations, the description discloses that the tool opens, reads, and closes the BC Companies page internally, and does not affect open pages or session state.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is clear and front-loaded, but includes a slightly redundant sentence about not using if company name is known. Still concise overall.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers purpose, return details, usage context, and behavioral side effects. No output schema exists, but description adequately explains what is returned.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

No parameters exist; schema coverage is 100%. Description states no parameters are required, which adds clarity beyond the empty schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool lists all companies and returns company names plus the active company. It distinguishes from sibling tools by specifying it is used before bc_switch_company.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use (before bc_switch_company to verify target company) and when not to use (if company name is known). Provides workflow guidance: use bc_switch_company then bc_open_page.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_lookupA

Enumerates candidate values for a related-table (FK) field by invoking BC's built-in Lookup on the field and returning the result rows. Use this when you need to see valid choices for a field before writing it with bc_write_data — for example, listing all Salesperson Codes before filling "Salesperson Code" on a Customer Card, or listing all Gen. Bus. Posting Groups before selecting one.

Use bc_lookup when the field has isLookup=true in the bc_open_page or bc_read_data response. The field must be on an open page (pageContextId from bc_open_page). The operation is non-mutating: it opens the lookup form and always cancels without selecting a value, leaving the source page field unchanged. Provide an optional search string to filter candidates (e.g., search:"AR" to narrow to codes starting with "AR").

Do NOT use bc_lookup for option/enum fields — those already expose their fixed choices in the options array of bc_open_page and bc_read_data responses. Do NOT use for fields where isLookup is false or absent. Do NOT use for fields that carry lookupCustom=true in the bc_open_page or bc_read_data response: isLookup=true together with lookupCustom=true means the field drives a custom AL OnLookup trigger that BC does not expose as an enumerable lookup form — bc_lookup returns a clear error for these. Use the field's own UI/AssistEdit instead.

Workflow: bc_open_page → inspect field isLookup=true → bc_lookup to list candidates → bc_write_data with chosen value.

ParametersJSON Schema
NameRequiredDescriptionDefault
fieldYesCaption of the field to enumerate lookup candidates for (e.g., "Salesperson Code", "Gen. Bus. Posting Group"). Must be an editable FK/related-table field that has a lookup (isLookup=true in bc_open_page or bc_read_data response).
searchNoOptional search string to filter candidates (e.g., "AR" to narrow to codes starting with AR). Applied via BC's native search on the lookup list. Omit to return all rows up to maxRows.
maxRowsNoMaximum number of candidate rows to return. Defaults to 50. Max 500. BC may return fewer if the table has fewer records.
pageContextIdYesPage context ID of the open page (card or list) returned by bc_open_page.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations, but the description discloses key behaviors: non-mutating, cancels without selecting, leaves field unchanged, returns error for lookupCustom fields. Does not cover edge cases like closed pages but covers major aspects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with main purpose and workflow, but includes some repetitive explanations. Still clear and well-organized despite moderate length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 4 parameters, no output schema, and no annotations, the description is thorough: explains preconditions, workflow, and behavioral results. Lacks detail on return format but sufficient for a lookup tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, and the description adds meaning: field must be editable FK, search uses BC's native search, maxRows default and max. Provides context beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool enumerates candidate values for a related-table (FK) field using BC's built-in Lookup. It provides specific examples like listing Salesperson Codes, and distinguishes itself from sibling tools by focusing on field enumeration.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly details when to use (field has isLookup=true) and when not to use (option/enum fields, isLookup false, lookupCustom=true). Provides workflow and alternative suggestions, making the decision clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_navigateA

Navigates to a specific record on an open Business Central List or Document page using its bookmark. Supports two actions: "select" positions the cursor on a row without opening it, and "drill_down" opens the record in its Card/Document page. Requires a pageContextId from bc_open_page and a bookmark from row data returned by bc_open_page or bc_read_data.

Action "select" (default): Positions the cursor on the specified row. Does NOT open the record or return new data -- it only moves the selection. Note: bc_execute_action can target a row directly via its own bookmark/rowIndex parameters, so you usually do not need a separate select before an action like Delete.

Action "drill_down": Opens the record's detail page (e.g., drilling down from Customer List opens Customer Card, drilling down from Sales Orders opens Sales Order). Returns a NEW pageContextId for the opened Card/Document page with its full state. The original List page remains open. Remember to bc_close_page both pages when done.

Section targeting: Use section (e.g., "lines") to navigate within a Document page's subpage repeater. Omit it for the header/default repeater.

Do NOT use this for Card pages -- it only works on pages with repeater rows. Do NOT confuse "select" with "drill_down": select just moves the cursor, drill_down opens a new page. For field-level lookups (enumerating valid values for a related-table field), use bc_lookup, not this tool.

Examples:

  • Select a row: { "pageContextId": "abc", "bookmark": "XXXX", "action": "select" }

  • Drill down to Card: { "pageContextId": "abc", "bookmark": "XXXX", "action": "drill_down" }

  • Drill down from a document line: { "pageContextId": "abc", "bookmark": "XXXX", "action": "drill_down", "section": "lines" }

ParametersJSON Schema
NameRequiredDescriptionDefault
actionNo"select" moves cursor to row (default). "drill_down" opens the record detail page (returns new pageContextId). For field lookups use the bc_lookup tool.
sectionNoSection containing the row (e.g., "lines" for document line items). Omit for header/default repeater.
bookmarkYesRow bookmark from bc_open_page or bc_read_data results identifying which record to navigate to.
pageContextIdYesPage context ID of the List or Document page containing the row to navigate to.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses behavioral traits: select does not open or return data, drill_down returns a new pageContextId, both pages remain open, and the need to close both. It also explains section targeting for subpages, leaving no ambiguity.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections for actions, examples, and targeting. However, it is somewhat lengthy; some redundancy could be trimmed without losing clarity. Still, it is effectively front-loaded with the main purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite no output schema, the description covers return behavior (new pageContextId for drill_down, none for select) and all required parameters. It addresses edge cases (section targeting, exclusion of Card pages), making the tool fully understandable for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value by providing examples, explaining the action enum in context, and clarifying the role of pageContextId and bookmark beyond the schema descriptions. Thus a score of 4 is warranted.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool navigates to a record on a List or Document page using a bookmark, and distinguishes two actions: select (cursor positioning) and drill_down (opens detail page). It differentiates from sibling tools (bc_lookup for field lookups, bc_execute_action for direct row targeting), making the purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides explicit guidance on when to use each action, includes examples, and explicitly states when not to use (e.g., Card pages, confusion between select and drill_down). It also directs to bc_lookup for field lookups, covering both positive and negative cases.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_open_pageA

Opens a Business Central page by its numeric page ID and returns its complete state as a list of sections. Each section has a sectionId, kind (header / lines / factbox / subpage / requestPage), caption, and the appropriate content shape. Card-shape sections (most headers, factboxes, requestPages) carry fields[] (and headers also carry actions[]). List-shape sections (lines, list-bodied headers, repeater subpages) carry rows[] and totalRowCount. The header section adapts to its page: it is card-shape on Card pages and list-shape on List pages -- the kind stays "header" either way for path stability. This is the entry point for interactive, page-scoped work -- it returns a pageContextId that the page-scoped tools (bc_read_data, bc_write_data, bc_execute_action, bc_navigate, bc_respond_dialog, bc_close_page, bc_lookup) take as input, plus a stateVersion you can pass as expectedStateVersion to bc_write_data / bc_execute_action to guard against stale state. (bc_query, bc_run_report, bc_search_pages, bc_list_companies, and bc_switch_company do NOT need a pageContextId.) For bulk, read-only data over standard entities, prefer bc_query -- it needs no open page. Use bc_search_pages first if you do not know the page ID for an entity.

Card pages (single-record views like Customer Card=21) return one header (card-shape) plus any FactBox sections attached to the page. List pages (Customer List=22) return a header (list-shape, rows[] populated). Document pages (Sales Order=42) return a header (card-shape), a "lines" list-shape section with the document lines, and any FactBoxes.

Option/enum fields and boolean fields in card-shape sections carry two extra properties: "options" (the allowed choices as [{text, value}]) and "selectedOption" (the currently chosen entry). Always use the "value" string from "options" as the SaveValue payload when writing an enum field -- do NOT guess or invent values. Example: Item Card "Type" field returns options=[{text:"Inventory",value:"0"},{text:"Service",value:"1"},{text:"Non-Inventory",value:"2"}] and selectedOption={text:"Inventory",value:"0"}.

Typical workflow: bc_open_page -> bc_read_data (refresh / filter / paginate a section) -> bc_write_data (edit fields in any section) -> bc_execute_action (post / release / delete) -> bc_close_page. Always call bc_close_page when done. Do NOT call this if the page is already open -- reuse the existing pageContextId. On BC Online the first call opens a local Microsoft sign-in window (password and Authenticator stay in that window; never pass them as tool arguments). Retry after completing sign-in.

Optional bookmark parameter opens a Card page to a specific record. Bookmarks come from list rows in any prior section.

Examples:

  • { "pageId": 22 } opens Customer List. Sections: [{ "sectionId": "header", "kind": "header", "rows": [...], "actions": [...] }] (no fields[] on a list-shape header).

  • { "pageId": 21, "bookmark": "..." } opens Customer Card. Sections include the header card plus FactBoxes (e.g. { "sectionId": "factbox:Customer Statistics", "kind": "factbox", "fields": [...] }).

ParametersJSON Schema
NameRequiredDescriptionDefault
pageIdYesNumeric BC page ID (e.g., 22 for Customer List, 21 for Customer Card). Use bc_search_pages to find IDs.
bookmarkNoOpen the page to a specific record. Bookmarks come from list row results in bc_open_page or bc_read_data.
tenantIdNoBC tenant ID. Defaults to the server-configured tenant. Only needed in multi-tenant deployments.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden, and it does so thoroughly: it explains the returned section structure, page-shape variations, pageContextId and stateVersion semantics, enum/options handling, the need to close the page, and the BC Online sign-in window. It also details what NOT to pass as arguments during sign-in. Very transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but well-structured and front-loaded with the core behavior. Every section earns its place: output shape, workflow, sign-in warning, and examples. It loses one point because it is verbose and partially repeats guidance already present in the schema, but the complexity of the tool justifies most of the length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema and no annotations, this description is remarkably complete for a complex, stateful tool. It covers return section kinds, shapes for card/list/document pages, enum field payload guidance, sibling-tool routing, workflow sequencing, resource cleanup, and authentication behavior. An agent has enough context to call this tool correctly and know what to expect.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already describes all three parameters at 100% coverage, so the baseline is 3. The description adds useful examples and workflow context, such as pageId examples and 'bookmarks come from list rows in any prior section,' but most of this reinforces rather than extends the schema. There is no significant new parameter semantic beyond what the schema already provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a precise verb and resource ('Opens a Business Central page by its numeric page ID') and immediately distinguishes this tool from siblings: it is the entry point for page-scoped work, while bc_query, bc_search_pages, bc_list_companies, etc. do not need a pageContextId. The examples for Customer List and Customer Card further disambiguate what the tool does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage guidance is explicit: prefer bc_query for bulk read-only data, use bc_search_pages first if the page ID is unknown, do not call this if the page is already open, always call bc_close_page when done, and a typical workflow is spelled out. It also warns about the sign-in window and retry behavior. This is strong, actionable when-vs-alternative guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_queryA

Reads records from Business Central in bulk using the Standard API v2.0 (OData/REST on port 7048). Use bc_query for efficient server-side filtered, sorted, and projected reads over many records — for example, fetching all open sales orders, listing customers in a city, or pulling G/L entries for a date range. This is far more efficient than using bc_open_page + bc_read_data for bulk reads because filtering and projection happen on the server before any data is transferred.

When to use bc_query: structured data retrieval over standard BC entities, when you need 2+ records with specific field selection, when you want server-side filter/sort/OData operators ($filter, $select, $top, $orderby, $expand), or when you need to inspect a large dataset without driving the BC UI. Entity names are BC Standard API v2.0 names (camelCase): customers, vendors, items, salesOrders, salesInvoices, purchaseOrders, purchaseInvoices, generalLedgerEntries, accounts, journals, journalLines, companies, employees, dimensions, dimensionValues, currencies, paymentTerms, shipmentMethods, paymentMethods, countriesRegions, unitsOfMeasure, taxGroups, contacts. Pass filter as OData $filter syntax (e.g., "city eq 'London'", "amount gt 1000", "postingDate ge 2024-01-01"). Pass select as comma-separated field names (e.g., "number,displayName,city") to limit response size. top defaults to 100 if omitted — pass explicitly to get more or fewer rows. Queries are company-scoped automatically; pass company to target a specific company (see bc_list_companies). The special "companies" entity is the one exception — it is the top-level environment list (not company-scoped), so the company parameter is ignored for it; query it to discover available companies.

When NOT to use bc_query: do not use for UI-driven flows (navigating pages, clicking buttons, filling forms — use bc_open_page + bc_execute_action for those). Do not use bc_query for posting, writing, or triggering BC business logic — OData reads are read-only; use bc_write_data and bc_execute_action for mutations. Do not use for custom/extension entities not in the Standard API v2.0 — those require the UI WebSocket tools. Auth: on-prem NavUserPassword uses HTTP Basic; BC Online (SaaS) uses device-code — when sign-in is needed the tool returns DEVICE_LOGIN_REQUIRED with a verification URL and code to show the user, and a retry after they sign in runs the query. bc_query does not need a /csh WebSocket session and does not open the SaaS sign-in window.

ParametersJSON Schema
NameRequiredDescriptionDefault
topNoMaximum number of rows to return. Defaults to 100 if omitted to prevent accidental full-table scans. Pass explicitly to get more rows.
entityYesBC Standard API v2.0 entity name (camelCase). Examples: customers, vendors, items, salesOrders, salesInvoices, purchaseOrders, generalLedgerEntries, accounts, companies, employees. See BC Standard API docs for the full list.
expandNoOData $expand for related entities. Examples: "salesLines", "customer($select=displayName)". Use sparingly — expanded entities increase response size significantly.
filterNoOData $filter expression for server-side filtering. Examples: "city eq 'London'", "amount gt 1000", "postingDate ge 2024-01-01 and postingDate le 2024-12-31", "contains(displayName, 'Contoso')". Applied by BC before returning data.
selectNoComma-separated OData $select field names to limit response size. Examples: "number,displayName,city", "id,amount,postingDate". Omit to return all fields.
companyNoOverride the BC company name for this query. Defaults to the server-configured company (BC_ODATA_COMPANY or first available company). Use when querying a specific company in a multi-company BC environment.
orderbyNoOData $orderby expression. Examples: "displayName asc", "postingDate desc", "amount desc,number asc".

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of disclosing behavior. It explicitly states the tool is read-only, describes the auth flow (Basic auth for on-prem, DEVICE_LOGIN_REQUIRED for SaaS), explains the default top=100, and notes company-scoping with the special exception for the 'companies' entity. It also warns that $expand increases response size. This is thorough and transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Despite being lengthy, the description is tightly organized with clear topic headers and no filler. Each sentence adds distinctive value: core functionality, entity list, parameter guidance, auth notes, and exclusions. The front-loading of purpose and the structured sections make it easy to scan.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is complex with 7 parameters and many integration details, but the description covers all essentials: what it reads, how filtering/projection work, entity naming, auth modes, default behavior, sibling alternatives, and the one special-case entity. It even addresses pagination via the top parameter. The absence of an output schema is mitigated by the clear 'reads records' semantics.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Although schema coverage is 100%, the description enriches each parameter with concrete OData syntax examples and operational semantics (e.g., 'top defaults to 100 if omitted', 'pass company to target a specific company'). It goes well beyond the schema's simple field explanations, making the parameter usage immediately actionable.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Reads records from Business Central in bulk using the Standard API v2.0 (OData/REST on port 7048).' It distinguishes from siblings by explicitly stating it is more efficient than bc_open_page + bc_read_data for bulk reads and by noting it does not use a WebSocket session. This leaves no ambiguity about what the tool does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description contains dedicated 'When to use' and 'When NOT to use' sections. It names specific alternatives for UI flows (bc_open_page + bc_execute_action) and mutations (bc_write_data and bc_execute_action), and even points to bc_list_companies for company discovery. This is exemplary guidance for tool selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_read_dataA

Refreshes a single section on an already-open page. Returns { section: { sectionId, kind, caption, fields?, rows?, actions?, totalRowCount? }, stateVersion }. Card-shape sections (header, factbox, requestPage) refresh their fields[]; list-shape sections refresh rows[]. The returned stateVersion can be passed as expectedStateVersion to bc_write_data / bc_execute_action to reject stale-state writes. Requires a pageContextId from a prior bc_open_page call.

Do NOT use this for bulk or analytical reads over standard entities (customers, items, ledger entries, ...) -- prefer bc_query, which reads server-side via OData with no open page and no UI paging. Use bc_read_data when you need the interactive page's exact rows, factboxes, or option metadata.

Pass section: "header" (default) to refresh the page's header. Pass section: "lines" to refresh document line items. Pass a factbox sectionId (e.g. "factbox:Customer Statistics", as listed in the bc_open_page response) to refresh the FactBox card.

Option/enum and boolean fields in card-shape sections carry "options" (allowed choices as [{text, value}]) and "selectedOption" (current choice). When writing an enum field with bc_write_data, use the "value" string from "options" -- do NOT guess values. Example: after opening Item Card, the "Type" field returns options=[{text:"Inventory",value:"0"},{text:"Service",value:"1"},{text:"Non-Inventory",value:"2"}]; to change to Service, write value "1".

Filtering applies to list-shape sections only. Pass an array of { column, value }; values use BC filter syntax (exact "10000", ranges "10000..20000", wildcards "consulting", expressions ">1000"). Multiple filters combine with AND.

clearFilters: true resets agent-applied filters and restores the page to its default/native filtered state before reading. Note: page-defined SourceTableView filters (set in AL code) remain active -- this does NOT guarantee a completely empty filter set. Use before applying new filters to avoid stacking. Applies to list-shape sections only. Runs before any filters[] in the same call.

Sorting: pass sort: { column, direction } to sort the repeater before reading. Applied server-side after any filters. Resets BC viewport to top of sorted result. "asc" = A-Z / 0-9, "desc" = Z-A / 9-0. The column must be a visible repeater column on the section. Non-sortable columns (FlowFields, BLOBs) may be rejected by BC with an error. Applies to list-shape sections only.

Column selection: pass columns: ["No.", "Name"] to limit the cells in each row, or the fields[] entries on a card section.

Range slicing: { offset, limit } returns rows[offset..offset+limit] for list sections. Use with totalRowCount for pagination.

Examples:

  • Refresh header: { "pageContextId": "abc" }

  • Filter customer list: { "pageContextId": "abc", "filters": [{ "column": "City", "value": "London" }] }

  • Sort by Name ascending: { "pageContextId": "abc", "sort": { "column": "Name", "direction": "asc" } }

  • Sort by Name descending: { "pageContextId": "abc", "sort": { "column": "Name", "direction": "desc" } }

  • Filter and sort: { "pageContextId": "abc", "filters": [{ "column": "City", "value": "London" }], "sort": { "column": "Name", "direction": "asc" } }

  • Clear filters and re-read: { "pageContextId": "abc", "clearFilters": true }

  • Clear and re-filter: { "pageContextId": "abc", "clearFilters": true, "filters": [{ "column": "City", "value": "London" }] }

  • Read sales order lines: { "pageContextId": "abc", "section": "lines" }

  • Refresh a FactBox: { "pageContextId": "abc", "section": "factbox:Customer Statistics" }

ParametersJSON Schema
NameRequiredDescriptionDefault
tabNoTab name to filter header fields by (e.g., "General", "Invoice Details", "Shipping and Billing"). Omit to return all header fields.
sortNoSort the repeater by a column before reading. Applied after filters, resets BC viewport to top of sorted result. Applies to list-shape sections only. Non-sortable columns (FlowFields, BLOBs) may be rejected by BC.
rangeNoSlice a subset of repeater rows. Returns rows[offset..offset+limit]. Use with totalRowCount for pagination.
columnsNoColumn caption names to include in results. Omit to return all columns. Reduces output size.
filtersNoServer-side filters to apply before reading. Multiple filters combine with AND logic.
sectionNosectionId to refresh. Defaults to "header". Examples: "lines" (document line items), "factbox:Customer Statistics" (FactBox). Listed in the bc_open_page sections array.
clearFiltersNoClears agent-applied filters and restores the page to its default/native filtered state. Page-defined SourceTableView filters (set in AL code) remain active — this is NOT a guaranteed blank filter set. Use before applying new filters to avoid stacking. Applies to list-shape sections only.
pageContextIdYesPage context ID returned by bc_open_page.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It thoroughly discloses return shape (section with fields/rows, stateVersion), behavior differences between card and list sections, filtering/sorting/clearFilters nuances, and limitations like SourceTableView filters remaining active. StateVersion usage for stale-state rejection is clearly explained.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is well-structured with clear sections and examples, but it is quite lengthy. While every part adds value, some redundancy exists (e.g., filter syntax repeated in schema and description). Front-loaded with core purpose, but could be slightly more concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, description adequately explains return values, all parameters, prerequisites, and error conditions (non-sortable columns). Includes practical examples covering common use cases. Covers all necessary aspects for correct tool invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but description adds substantial context beyond schema: default section='header', how options work for enum fields, filter syntax, sorting details, clearFilters caveats. This significantly aids correct parameter usage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States exactly what the tool does: 'Refreshes a single section on an already-open page.' Clearly distinguishes from sibling tools like bc_query and bc_write_data by specifying when to use each.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use and when-not-to-use guidance: 'Do NOT use this for bulk or analytical reads... prefer bc_query' and 'Use bc_read_data when you need the interactive page's exact rows, factboxes, or option metadata.' Also notes prerequisite of pageContextId from bc_open_page.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_respond_dialogA

Responds to an open Business Central dialog or confirmation prompt. Dialogs are triggered by bc_execute_action, bc_write_data, or bc_run_report when BC requires user input (e.g., "Do you want to post?", "Delete this record?", validation warnings, or a report request page). When those tools return a dialogsOpened array with requiresDialogResponse: true, or bc_run_report returns a requestPage, you MUST call this tool (response: "ok" for a report request page) to continue the workflow.

The dialogFormId comes from the dialogsOpened array in the triggering tool's response. The response parameter accepts: "ok" (confirm/accept), "cancel" (dismiss/abort), "yes" or "no" (answer a yes/no question), "abort" (force-close), or "close" (close a modal information page). Choose the response that matches the dialog's intent -- confirmation dialogs typically need "yes", acceptance dialogs need "ok".

After responding, check the changedSections array in the result to see which page sections were affected. For example, posting a Sales Order may change all sections. If the dialog response triggers another dialog (chained confirmations), the response will include a new dialogsOpened array -- respond to each dialog in sequence.

Do NOT call this without a preceding dialog -- there is no dialog to respond to unless dialogsOpened was returned by bc_execute_action / bc_write_data, or a requestPage was returned by bc_run_report. Do NOT guess the dialogFormId -- always use the exact value from the dialogsOpened array (or requestPage.formId).

Example: { "pageContextId": "abc", "dialogFormId": "dialog-123", "response": "yes" }

If the action produces a file (Open in Excel, Print, export), its bytes appear in downloads[]; links BC would open externally appear in externalUris[] and are never fetched by the server.

ParametersJSON Schema
NameRequiredDescriptionDefault
responseYes"ok" confirms, "cancel" dismisses, "yes"/"no" answers a question, "abort" force-closes, "close" closes a modal info page.
dialogFormIdYesDialog form ID from the dialogsOpened array returned by bc_execute_action or bc_write_data, or requestPage.formId returned by bc_run_report.
pageContextIdYesPage context ID of the page that triggered the dialog.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. Discloses effect on workflow, chained dialogs, side effects (changedSections, downloads, externalUris). Warns against guessing dialogFormId. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is detailed but front-loaded with main purpose. All sentences are necessary for complex workflow; could be slightly more concise but not overly verbose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers all aspects: workflow usage, parameter sources, chained dialogs, side effects like downloads/URIs. No output schema but explains expected result fields adequately.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, but tool description adds context: how to obtain dialogFormId and pageContextId from previous responses, and clarifies response enum options beyond schema. Adds meaningful value.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states it responds to dialogs triggered by other tools (bc_execute_action, bc_write_data, bc_run_report). Differentiates from siblings by specifying its role as a follow-up to dialog triggers.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to call (upon dialogsOpened or requestPage) and when not to call (without preceding dialog). Provides examples, mentions chained dialogs, and explains response options.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_run_reportA

Execute a Business Central report by its numeric report ID. If the report has a request page (parameter/filter dialog), the response's requestPage carries its fields plus a requestPage.pageContextId and requestPage.formId. Fill parameters with bc_write_data against that pageContextId, then run the report with bc_respond_dialog { dialogFormId: requestPage.formId, response: "ok" }. The report runs server-side on the BC service tier.

Pass format: "pdf", "excel", or "word" to capture the rendered output as base64-encoded bytes (this path auto-drives the request page, so no bc_write_data/bc_respond_dialog is needed). The tool drives the BC "Send to..." flow (SystemAction 410) internally: opens the format-selection dialog, selects the requested format by SaveValue-ing the matching text label into the SelectionControl, confirms with OK (300), then fetches the file from DynamicFileHandler.axd. When format is set, rendered file bytes are returned in downloads[] (base64 in bytes, or savedPath when BC_DOWNLOAD_DIR is set); an oversized file reports a per-entry error instead of bytes.

Format availability depends on the report's installed layouts -- not all reports offer all three formats. If the report does not offer the requested format, an error is returned listing the available option texts. "pdf" is always BC's default and requires no SaveValue; "excel" prefers the "data only" variant; "word" targets any option containing "Word".

Use this tool for reports that perform server-side actions (batch posting via Report 295, inventory adjustments, data processing) or to inspect and fill request page parameters. Common reports: 1306 (Customer Statement), 120 (Aged Accounts Receivable), 6 (Trial Balance), 295 (Batch Post Sales Orders).

Do NOT use this for viewing data -- use bc_open_page and bc_read_data for data retrieval. Do NOT confuse reports with pages -- reports are processing/printing objects, pages are UI views.

Example (open request page): { "reportId": 6 } Example (capture PDF): { "reportId": 6, "format": "pdf" } Example (capture Excel): { "reportId": 6, "format": "excel" } Example (capture Word): { "reportId": 6, "format": "word" }

ParametersJSON Schema
NameRequiredDescriptionDefault
formatNoRendered output format to capture via the BC "Send to..." flow. "pdf" captures a PDF (BC default); "excel" captures Excel (prefers "data only" layout); "word" captures a Word document. Format availability depends on the report's installed layouts -- reports without the requested layout return an error listing available formats. Omit to open the request page only without executing.
reportIdYesNumeric BC report ID to execute (e.g., 1306 for Customer Statement, 6 for Trial Balance).

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden, and it delivers: it discloses server-side execution, internal driving of the BC 'Send to...' flow (SystemAction 410), request-page auto-driving when format is set, oversized-file error behavior, and layout-dependent format availability. This is a high-transparency description for a complex mutation-like tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but every section earns its place: summary, request-page workflow, format mechanics, layout caveats, usage boundaries, and concrete examples. The structure front-loads the core behavior and then layers detail logically, making it easy for an agent to extract the critical decision points quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given there is no output schema, the description adequately explains return behavior (downloads[] with base64 bytes or savedPath, oversized-file errors, requestPage details). It also covers edge cases like unavailable formats and defaults, making it complete enough for an agent to invoke the tool correctly in most scenarios.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Although input schema coverage is 100%, the description adds substantial meaning beyond the schema: examples of valid invocations, what omitting format does, how format maps to BC's 'Send to...' flow, and how errors surface when a format is unavailable. This goes well beyond the baseline set by the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action—execute a Business Central report by numeric report ID—and clearly distinguishes reports from pages and from data-viewing tools. It also names sibling tools (bc_open_page, bc_read_data) as the alternatives for data retrieval, so the tool's role is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says when to use this tool (server-side report actions, inspecting/filling request page parameters) and when not to use it (viewing data—use bc_open_page/bc_read_data instead). It also provides common report IDs and explains the format-selection behavior, leaving little ambiguity about correct invocation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_search_pagesA

Searches BC's Tell Me index for pages, reports, codeunits, and other run-targets matching the query. Each result is { name, objectType, runTarget, departmentPath?, category?, score? } where objectType is "page" / "report" / "codeunit" / etc., runTarget is the BC AL object name (e.g. "Customer List"), and category is the BC department (e.g. "Lists", "Tasks"). Use this when you do not know the page ID for an entity — search by keyword first, then resolve. Do NOT use it when you already know the numeric page ID (call bc_open_page directly), and do NOT use it to read data — it only discovers objects.

Tell Me is PROFILE-SCOPED on the BC server. If the search returns no rows in an env where the BC web client finds matches, set the BC_PROFILE environment variable on bc-mcp's startup config to a profile that indexes the relevant objects (BUSINESS MANAGER, ACCOUNTANT, SALES ORDER PROCESSOR, etc.). The default profile may have an empty Tell Me index.

Note that BC's Tell Me identifies pages by AL name, not by numeric ID. The runTarget is therefore a string like "Customer List" rather than "22". To open the result, the caller currently still needs the numeric page ID: match the runTarget AL name to a known page ID (e.g. "Customer List" = 22), or try bc_open_page with a candidate ID.

Empty-result behavior: response includes a "note" string explaining the likely cause and suggesting BC_PROFILE remediation.

Examples:

  • { "query": "customer" } returns rows like { "name": "Customers", "objectType": "page", "runTarget": "Customer List", "category": "Lists", "score": 9 }.

  • Empty case: { "results": [], "note": "No results. Tell Me is profile-scoped..." }.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesSearch term matching BC page names and keywords (e.g., "customer", "sales order", "chart of accounts"). Fuzzy matching supported.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses behavioral traits: profile scoping, return format including a note for empty results, AL names vs numeric IDs, and the need to map runTarget to page IDs for opening. This is comprehensive and transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-organized with front-loaded purpose and clear sections, but it is somewhat lengthy. Each paragraph adds important context, so no waste, but could be slightly more concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (Tell Me index, profile scoping, AL name resolution) and lack of output schema, the description covers return structure, empty result behavior, and example use cases, making it fully complete for an agent to use correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter (query) with schema coverage 100%. The description adds value by providing example queries ('customer', 'sales order') and noting fuzzy matching, going beyond the schema's basic description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states that the tool searches BC's Tell Me index for pages and other run-targets, specifying the resource and return types. It distinguishes from siblings like bc_open_page and bc_read_data, making the purpose specific and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit guidance on when to use (unknown page ID) and when not (known numeric page ID or reading data). It also explains profile scoping and suggests BC_PROFILE remediation for empty results, providing clear context for usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_switch_companyA

Switch to a different company within the current Business Central session. All currently open pages will be invalidated and their pageContextIds will become unusable -- you must call bc_open_page to re-open any pages you need in the new company context.

Use bc_list_companies first to see the available company names and verify the target company exists. The companyName must be an exact match. After switching, all subsequent bc_open_page, bc_read_data, bc_write_data, and bc_execute_action calls will operate against the new company's data.

Do NOT switch companies in the middle of a multi-step workflow (e.g., between creating a Sales Order and posting it). Complete all operations in the current company first, then switch.

Example: { "companyName": "CRONUS International Ltd." }

ParametersJSON Schema
NameRequiredDescriptionDefault
companyNameYesExact company name to switch to. Use bc_list_companies to see available company names.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. It fully discloses that all open pages are invalidated, pageContextIds become unusable, and subsequent operations target the new company. The mid-workflow warning adds critical behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured: first the purpose, then behavioral effects, usage guidelines, and an example. Every sentence adds value, and the length is appropriate for the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's impact (session state change), the description covers all necessary context: prerequisites (list companies), exact match requirement, effect on pages, and workflow restrictions. No output schema needed; the description is complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with a basic description. The description adds value by stating 'exact match', directing to use bc_list_companies, and providing an example. This goes beyond the schema's minimal description, though not dramatically so.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Switch to a different company within the current Business Central session' using a specific verb and resource. It distinguishes itself from siblings like bc_list_companies (which lists companies) and bc_open_page (which opens pages), making the tool's unique role clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit when-to-use guidance: use bc_list_companies first, require exact match, and warns against switching mid-multi-step workflow. It also explains the effect on pages, offering clear alternatives and restrictions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_wizard_navigateA

Drive a Business Central NavigatePage / wizard by semantic step. Use after bc_open_page on a page whose response has isModal: true and pageType: "NavigatePage" (Continia activation wizards, BC setup wizards, request pages with multi-step layouts). The action argument is one of: "next" (advance), "back" (return to previous step), "finish" (complete the wizard), "cancel" (abort).

bc-mcp identifies the navigation buttons by the icon resource BC's own client uses (Actions/PreviousRecord, Actions/NextRecord, Actions/Approve), not by SystemAction or caption -- so localised wizards work without changes. The response surfaces fields visible on the new step, the remaining navigation options (availableNav), and a closed flag set when the wizard finished.

Typical workflow: bc_open_page (returns isModal=true, fields for step 0) -> bc_write_data (fill step 0 inputs) -> bc_wizard_navigate { action: "next" } -> bc_write_data (fill step 1) -> ... -> bc_wizard_navigate { action: "finish" }. The wizard closes itself on finish/cancel; the pageContextId becomes invalid afterwards.

Do NOT use this for non-wizard pages -- use bc_execute_action instead. Do NOT call "next" past the last step -- use "finish" once availableNav lists it.

Example: { "pageContextId": "abc", "action": "next" }

If the action produces a file (Open in Excel, Print, export), its bytes appear in downloads[]; links BC would open externally appear in externalUris[] and are never fetched by the server.

ParametersJSON Schema
NameRequiredDescriptionDefault
actionYesWizard step navigation. "next" advances, "back" returns to previous step, "finish" completes the wizard, "cancel" aborts.
pageContextIdYesPage context ID returned by bc_open_page for a NavigatePage / wizard.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It discloses how navigation buttons are identified (by icon resource, not SystemAction/caption), localised wizards work, response fields (fields, availableNav, closed flag), and special handling for file downloads and external URIs. Also notes pageContextId becomes invalid after finish/cancel.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with main purpose, mechanism details, workflow, dos/don'ts, example, and edge cases. Every sentence adds value, though it could be slightly more concise. Front-loaded with purpose and usage conditions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of wizard navigation with state, the description covers usage, behavior, parameters, output (including download and external URI handling), and lifecycle of pageContextId. No output schema exists, but response fields are described sufficiently.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema covers both parameters with descriptions (100% coverage). Description adds context: action values are listed with their effects, pageContextId is from bc_open_page for a NavigatePage, and provides an example. Adds workflow guidance on when to use each action, which is valuable beyond schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool drives a Business Central NavigatePage/wizard by semantic step, specifying the context after bc_open_page on a NavigatePage. It lists the four possible actions and distinguishes from sibling tool bc_execute_action.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use (after bc_open_page on a NavigatePage with isModal true and pageType NavigatePage) and when not to (non-wizard pages, use bc_execute_action instead). Provides a typical workflow and warns not to call 'next' past the last step.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

bc_write_dataA

Writes one or more field values on an already-open Business Central page. Pass a fields object with caption-name keys and string values. BC validates each field and returns the server-confirmed value, which may differ from input due to formatting, auto-completion, or lookups (e.g., entering a partial customer name resolves to the full match). Requires a pageContextId from bc_open_page.

Fields must be editable -- writing to a read-only field returns an error. Write related fields together in one call (e.g., quantity and unit price), but avoid writing unrelated groups together because BC validation cascades may change dependent fields in unexpected order. Check the returned confirmed values to see what BC actually stored.

For Document page line items (Sales Order lines, Purchase Order lines), specify section: "lines" to write to the lines repeater. Use rowIndex (0-based row position) or bookmark (stable row identifier from bc_read_data results) to target a specific line. Prefer bookmark over rowIndex when rows may have been reordered or inserted since the last read.

Pass expectedStateVersion (from a prior bc_read_data or bc_open_page stateVersion field) to guard against acting on drifted state. If the page has been mutated by async events or a sibling operation since that read, the call is immediately rejected with code STALE_CONTEXT before touching BC. Re-read with bc_read_data to get the current stateVersion, then retry. Omit expectedStateVersion to skip the check.

Do NOT use this for triggering actions like Post, Delete, or Release -- use bc_execute_action instead. Do NOT use this for navigating to records -- use bc_navigate instead.

Examples:

  • Write to Card header: { "pageContextId": "abc", "fields": { "Name": "Contoso Ltd", "Address": "123 Main St" } }

  • Write to Sales Order line: { "pageContextId": "abc", "section": "lines", "rowIndex": 0, "fields": { "Quantity": "5", "Unit Price": "100" } }

  • Write with bookmark targeting: { "pageContextId": "abc", "section": "lines", "bookmark": "XXXX", "fields": { "Description": "Consulting Services" } }

  • Write with staleness guard: { "pageContextId": "abc", "fields": { "Name": "Contoso" }, "expectedStateVersion": 3 }

ParametersJSON Schema
NameRequiredDescriptionDefault
fieldsYesKey-value pairs of field caption names and string values to write (e.g., { "Name": "Contoso", "City": "London" }).
sectionNoSection to write to (e.g., "lines" for document line items). Omit for header fields.
bookmarkNoStable row identifier from bc_read_data results. Preferred over rowIndex when rows may be reordered.
rowIndexNo0-based row position in the repeater to write to. Use for line items. Prefer bookmark for stability.
pageContextIdYesPage context ID returned by bc_open_page.
expectedStateVersionNoOpt-in staleness guard. Pass the stateVersion from a prior bc_read_data or bc_open_page response. If the page state has changed since that read (async events or sibling writes mutated it), the call is rejected immediately with code STALE_CONTEXT before touching BC. Omit to skip the check.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, but description fully covers behavioral traits: BC validation, server-confirmed values, read-only field errors, cascading updates, and staleness guard with expectedStateVersion.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear sections, explicit do/don't, and four relevant examples. Every sentence is informative without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite no output schema, description explains return value (server-confirmed value) and staleness rejection. Complex tool (6 params, nested objects) is fully covered with usage details.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but description adds significant context beyond schema, such as how to use section, rowIndex, bookmark, and expectedStateVersion, plus examples. Baseline 3, increased due to extra guidance.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool writes field values on an already-open BC page, distinguishes from siblings like bc_execute_action and bc_navigate, and includes specific usage cases.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly tells when to use (write fields) and when not to (actions, navigation), with alternative tools named. Provides guidance on grouping related fields and using bookmark over rowIndex.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 2 tool updatesv1.7.0
    • Changedbc_open_page2 fields changed
      • removedInput schema / properties / pageId / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "number"
        -  }
        -]
      • addedInput schema / properties / pageId / type
        Added value: +[
        +  "string",
        +  "number"
        +]
    • Changedbc_run_report2 fields changed
      • removedInput schema / properties / reportId / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "number"
        -  }
        -]
      • addedInput schema / properties / reportId / type
        Added value: +[
        +  "string",
        +  "number"
        +]
  2. 14 tool updatesv1.5.0
    • First observedbc_close_page
    • First observedbc_execute_action
    • First observedbc_list_companies
    • First observedbc_lookup
    • First observedbc_navigate
    • First observedbc_open_page
    • First observedbc_query
    • First observedbc_read_data
    • First observedbc_respond_dialog
    • First observedbc_run_report
    • First observedbc_search_pages
    • First observedbc_switch_company
    • First observedbc_wizard_navigate
    • First observedbc_write_data

TDQS

A4.8/5.0
Disambiguation5/5

Each tool has a distinct and unique purpose with no functional overlap. For example, bc_read_data refreshes UI sections while bc_query performs bulk OData reads, and bc_execute_action triggers actions while bc_wizard_navigate drives wizard steps. Descriptions clearly differentiate when to use each tool.

Naming Consistency5/5

All tools follow the 'bc_' prefix with consistent snake_case naming. The pattern is verb_noun (e.g., bc_open_page, bc_write_data, bc_list_companies) with clear, descriptive names. No mixed conventions or ambiguous abbreviations.

Tool Count5/5

With 14 tools, the set covers the essential operations for interacting with Business Central (page management, data reading/writing, actions, reports, company switching, lookup, search) without being bloated or sparse. Each tool serves a well-defined role in the workflow.

Completeness5/5

The tool surface provides comprehensive coverage of Business Central interactions: opening, reading, writing, navigating, executing actions, running reports, handling dialogs, switching companies, and querying via OData. The workflow from discovery (bc_search_pages) through lifecycle is complete with no obvious gaps.

Maintenance

ActivityActive
ResponsivenessResponsive

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Gives AI assistants direct access to Microsoft Dynamics 365 Business Central via the native WebSocket protocol, replacing OData, APIs, and browser automation. Enables page navigation, data reading/writing, actions, searches, and report execution.
    14
    57
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SShadowS/business-central-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server