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.7/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 does so thoroughly. It discloses side effects: the pageContextId becomes invalid, subsequent calls will fail, it is safe after prior errors, and it frees server-side resources including the WebSocket session. This goes beyond basic existence to explain behavioral implications.

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 efficiently structured into three focused paragraphs: core action, invalidation and safety, and exclusions. Every sentence earns its place, providing critical operational details without unnecessary verbosity.

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 having no output schema, the description is complete for the tool's complexity. It covers lifecycle, error handling, multi-step workflow constraints, and even names sibling tools for context. An agent has sufficient information to invoke this tool correctly and understand its consequences.

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 schema already documents the single parameter with 100% coverage, including its origin and invalidation. The description reinforces this ('Requires a pageContextId from bc_open_page') but adds no new parameter-specific semantics beyond reinforcing the schema, 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's function: 'Closes an open Business Central page and frees its server-side resources including the WebSocket form session.' This uses a specific verb (closes) and resource (Business Central page), and it distinguishes from sibling tools like bc_read_data or bc_execute_action by focusing on cleanup and resource management.

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 guidance: 'Always call this when you are finished working with a page to prevent resource leaks,' along with clear exclusions such as 'Do NOT call this in the middle of a multi-step workflow' and 'Do NOT call this to reset a page; use bc_read_data to refresh data instead.' It also addresses closing drill-down pages, making usage unambiguous.

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.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 the full burden. It discloses that the tool internally opens the Companies system page, reads entries, and closes it, and explicitly states it does not affect current open pages or session state. This is thorough behavioral disclosure.

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 compact, with only three sentences plus a brief exclusionary note. Every sentence provides necessary value: purpose, usage guidance, behavioral transparency, and parameter clarity.

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 simplicity (zero parameters, no output schema), the description fully covers its purpose, return value, side effects, and relationship to sibling tools. It is complete enough for an agent to select and invoke it 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?

There are no parameters, and the schema description coverage is 100% with zero properties. The description explicitly notes 'No parameters are required,' which is sufficient given there are no parameters to explain.

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 in the Business Central environment and returns an array of company names plus the active company. This distinguishes it from the sibling tool bc_switch_company, which is for switching rather than listing.

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 says to use before bc_switch_company to verify existence, and warns not to use it if the company name is already known, directing to bc_switch_company directly. It also offers guidance for subsequent actions like bc_switch_company followed by 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.8/5.0
Behavior5/5

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

With no annotations provided, the description fully carries the behavioral burden. It clearly states the operation is non-mutating: 'opens the lookup form and always cancels without selecting a value, leaving the source page field unchanged.' It also discloses edge-case behavior for lookupCustom=true and maxRows behavior, adding significant transparency beyond the schema.

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 longer than the ideal two-sentence example, but it is well-structured with front-loaded purpose, then usage rules, negative cases, and workflow. Some repetition of the isLookup=true condition occurs, but overall every section earns its place and contributes distinct information.

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 having no output schema and no annotations, the description covers prerequisites, exact usage conditions, exclusions, error behavior, search semantics, row limits, and a workflow. It is complete enough for an agent to safely decide whether to invoke this tool and what to expect in return.

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?

The input schema already has 100% description coverage for all four parameters, so the baseline is 3. The description adds meaningful extra context by explaining how search filters candidates, confirming pageContextId comes from bc_open_page, and clarifying maxRows defaults and BC's ability to return fewer rows. This pushes it above baseline.

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: 'Enumerates candidate values for a related-table (FK) field by invoking BC's built-in Lookup on the field and returning the result rows.' It also distinguishes itself from siblings by framing this as a pre-write validation step for bc_write_data, with concrete examples like listing Salesperson Codes.

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 gives explicit when-to-use guidance (when isLookup=true and the field is on an open page), explicit when-not-to-use guidance (option/enum fields, isLookup=false, lookupCustom=true), and even provides a workflow sequence. It names alternatives indirectly by stating what bc_lookup is not for, which is clear enough.

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.9/5.0
Behavior5/5

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

With no annotations provided, the description carries full responsibility. It discloses that select does not open or return data, that drill_down returns a new pageContextId and leaves the original page open, that both pages should be closed, and that the tool only works on repeater pages. This is exceptionally 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?

The description is long but every section earns its place. It is organized with action-specific paragraphs, a separate section for section targeting, explicit warnings, and illustrative JSON examples. No redundancy or filler sentences are present.

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 has moderate complexity (two actions, section targeting, page context lifecycle). The description covers prerequisites (pageContextId, bookmark sources), behavior of each action, the need to close both pages, and exclusions. Even without an output schema, it explains return behavior for drill_down and the lack of return for select, making it completely self-contained.

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 the baseline is 3. The description adds value by explaining the behavioral difference between the two action enum values, clarifying the source of the bookmark (from bc_open_page or bc_read_data), and giving concrete examples of section usage. This elevates it above baseline.

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+resource statement: 'Navigates to a specific record on an open Business Central List or Document page using its bookmark.' It then details two distinct actions (select and drill_down), which clearly differentiates it from siblings like bc_execute_action and bc_lookup.

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 includes explicit when-to-use and when-not-to-use guidance: it warns against using select before actions (pointing to bc_execute_action), says not to use the tool for Card pages, and directs field lookups to bc_lookup. These alternatives are named directly.

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

A5/5.0
Behavior5/5

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

With no annotations, the description carries the full burden and does so exceptionally. It discloses return shape, stateVersion semantics for stale-state rejection, section behaviors (card vs list), filter restrictions (list-shape only), clearFilters behavior (does not clear SourceTableView filters), sorting server-side behavior, and range slicing. This is far beyond the schema.

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 sentence earns its place. It is front-loaded with the core purpose, followed by structured paragraphs for each parameter and a comprehensive set of examples. The structure allows for easy scanning, and there is no fluff or repetition.

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?

For a tool with 8 parameters, nested objects, and no output schema, the description is remarkably complete. It covers the return structure, parameter semantics, edge cases (non-sortable columns, SourceTableView filters), and provides 9 examples covering different use cases. It fully compensates for the lack of an output schema.

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 description coverage is 100%, the description adds significant meaning beyond the schema. It provides concrete examples for filters, sort, range, columns, and clearFilters, explains filter syntax in detail, clarifies ordering (clearFilters before filters), and gives real-world examples like FactBox section IDs. This exceeds the baseline of 3.

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+resource: 'Refreshes a single section on an already-open page' and clearly differentiates from siblings like bc_query by stating 'Do NOT use this for bulk or analytical reads... prefer bc_query.' It also explains when to use bc_read_data for interactive page rows, factboxes, or option metadata.

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 and when-not-to-use guidance: it forbids bulk reads in favor of bc_query and states 'Use bc_read_data when you need the interactive page's exact rows, factboxes, or option metadata.' It also notes the prerequisite of a pageContextId from a prior bc_open_page call, giving clear context.

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.9/5.0
Behavior5/5

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

With no annotations, the description fully discloses critical behaviors: profile-scoped Tell Me index, empty-result handling with a note, runTarget being AL name not numeric ID, and the need to map to a page ID afterward. It also shows example output and empty-case behavior, which is transparent and helpful.

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 detailed but tightly structured. It leads with the core purpose, then adds usage guidance, caveats, examples, and edge-case behavior. Every sentence provides useful context 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 having only one parameter and no output schema, the description covers the tool's purpose, output format, failure mode, profile-scoping issue, and examples. It is self-contained and leaves no significant gaps for an agent to misuse the 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?

The input schema already describes the query parameter well (100% coverage). The description adds value with concrete examples and clarifies that the query is for object discovery, but it mostly reinforces schema content. Still, the extra examples and explanation elevate it above baseline.

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 searches BC's Tell Me index for pages, reports, codeunits, and other run-targets. It distinguishes itself from siblings by explicitly noting it is for discovery, not for reading data or opening pages when the ID is known.

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 guidance (when page ID is unknown, search by keyword first) and when-not-to-use (when numeric ID is known, call bc_open_page directly; not for reading data). It also gives context about profile-scoped behavior and BC_PROFILE remediation, which is practical usage guidance.

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, case-sensitive match (a wrong-case or unknown name returns COMPANY_NOT_FOUND and leaves the session on its current company). 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 the full burden—and it delivers. It discloses that open pages are invalidated, pageContextIds become unusable, unknown names return COMPANY_NOT_FOUND without changing the company, and all later data/action calls target the new company. This is rich behavioral disclosure beyond the basic mutation.

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 longer than average, but every sentence earns its place: purpose, critical invalidation side effect, prerequisite, failure behavior, post-switch scope, and a workflow warning. It is front-loaded with the most important consequence and uses structure to emphasize the 'do NOT' case.

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?

For a one-parameter, high-stakes session-switching tool with no annotations and no output schema, the description is complete. It covers prerequisite, exact input requirements, failure mode, side effects, and safe workflow usage. Nothing essential is missing for an agent to invoke it 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?

The schema already covers the single parameter at 100%, so the baseline is 3. The description adds meaningful semantics: exact case-sensitivity, error behavior for wrong-case names, and a concrete example. This goes beyond the schema's minimal 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 the verb and resource: 'Switch to a different company within the current Business Central session.' It is explicitly distinguished from siblings like bc_list_companies, which lists companies rather than switching to one. The purpose is unambiguous and not a tautology.

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 gives strong usage guidance: call bc_list_companies first to verify the target, require an exact case-sensitive match, and avoid switching mid-workflow. It explicitly warns against a common misuse case and explains the session-wide effect on subsequent tool calls.

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?

With no annotations, the description carries the full burden and does so thoroughly: details return values (server-confirmed may differ), errors on read-only fields, validation cascades, expectedStateVersion stale-context rejection (STALE_CONTEXT), and bookmark vs rowIndex behavior. This exceeds what annotations would typically provide.

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?

Although long, every section earns its place: core purpose, parameter details, usage examples, and explicit exclusions. Front-loaded with the primary function and structured in clear paragraphs with bullet-like examples. No wasted words.

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?

This tool has high complexity (6 params, nested objects, no output schema, no annotations). The description covers all aspects: prerequisites, return value behavior, error conditions, line-item targeting, staleness guard, and exclusions. It is fully self-contained for correct selection and 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?

The input schema already has 100% coverage with descriptions for all parameters. The description adds extra semantic value beyond the schema, especially for expectedStateVersion (explains the stale guard and error behavior), bookmark (notes it is preferred over rowIndex), and section (what 'lines' means). This nudges above the schema-only baseline.

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+resource+scope: 'Writes one or more field values on an already-open Business Central page.' It also clarifies what it is not for by naming sibling tools (bc_execute_action, bc_navigate), fully distinguishing its purpose from alternatives.

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. It states the prerequisite (requires pageContextId from bc_open_page), advises grouping related fields, warns against unrelated groups, and explicitly says 'Do NOT use this for triggering actions... use bc_execute_action instead' and 'Do NOT use this for navigating... use bc_navigate instead.'

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.

  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.9/5.0

Scored across 14 tools

Disambiguation5/5

Every tool has a distinct, well-documented purpose with explicit guidance on when to use it over alternatives (e.g., bc_query vs bc_read_data, bc_execute_action vs bc_wizard_navigate). The descriptions include 'Do NOT use' clauses that eliminate ambiguity.

Naming Consistency5/5

All tools follow the 'bc_' prefix plus a clear verb_noun pattern (run_report, open_page, write_data, respond_dialog, read_data, list_companies, execute_action, close_page, search_pages, switch_company, wizard_navigate). The two short verbs (navigate, lookup) still fit the pattern and are not confusing.

Tool Count5/5

14 tools is well-scoped for an ERP integration server. Each tool covers a distinct aspect of Business Central interaction without redundancy, and the count is within the ideal 3-15 range.

Completeness5/5

The tool set provides comprehensive lifecycle coverage: discovery (search_pages, list_companies), opening pages, reading data (query, read_data), writing, executing actions, handling dialogs, navigating, and session management. It supports both UI-driven workflows and efficient OData bulk reads, leaving no obvious dead ends.

Maintenance

ActivityActive
ResponsivenessResponsive

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
    177 npm
    MIT