business-central-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@business-central-mcpShow me the open sales orders list"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Overview
Property | Value |
Language | TypeScript / Node 20+ |
npm package | |
BC versions | BC27, BC28 (wire-compatible) |
Auth | NavUserPassword (OAuth on roadmap) |
Tools | 12 |
Tests | 284 unit/protocol + 111 integration |
License | MIT |
Related MCP server: Microsoft Business Central MCP Server
Install
VSCode
Click the badge. VSCode opens, prompts to add the server, and writes to your user mcp.json.
You will still need to set BC_BASE_URL, BC_USERNAME, and BC_PASSWORD in the entry's env block. VSCode opens the file for you to edit.
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"
}
}
}
}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-mcpScope it to the current project with --scope project. See claude mcp --help for scoping options.
Claude Desktop
Download the latest
.dxtfrom Releases.Double-click. Claude Desktop opens Settings → Extensions and prompts for BC URL, username, and password.
Restart Claude Desktop.
Edit claude_desktop_config.json:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.jsonLinux:
~/.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 |
| Yes | — | BC server base URL, e.g. |
| Yes | — | NavUserPassword username |
| Yes | — | NavUserPassword password |
| No | server default | Profile id, e.g. |
| No |
| Multi-tenant deployments only. |
| No |
| Version reported to BC during session open. |
| No |
| HTTP transport port (stdio transport ignores this). |
| No |
|
|
| No |
| Directory for log files. |
| No |
| Directory for session state. |
| No |
| Per-invoke timeout in ms. Kills hung sessions. |
| No |
| Reconnect attempts after session death. |
| No |
| Base delay (ms) for exponential reconnect backoff. |
What can it do?
Tool | What it does |
| Open any page by ID -- lists, cards, documents, role centers. Returns the page as |
| Refresh a single section: filter, paginate, slice, project tab/columns. Returns the same |
| Write field values; BC validates and echoes confirmed values. Section-aware (lines, factboxes, header). |
| Run header / row / wizard actions, OR drill down on Role Center cue tiles via |
| Handle confirmation prompts and request pages |
| Select rows, drill down into records, field lookups |
| Tell Me search. Returns |
| Close a page and free server resources |
| Switch to a different company mid-session |
| Discover available companies |
| Execute reports and fill request page parameters |
| 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 Serverbc_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 (
headeron Card pages,factbox,requestPage):fields[]and (forheader)actions[]List-style (
lineson Documents,headeron List pages, repeater subpages):rows[]andtotalRowCountCue tiles (Role Center hosted CardParts):
cues[]with each tile'sname,value,groupCaption,synopsis,hasAction. Drill down withbc_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
LogicalModalityViolationExceptionmid-session: reconciles the modal stack and retries transparently; falls back to session reset when BC keeps a confirm dialog sticky
Key files
File | Purpose |
| npm |
| HTTP MCP transport entry |
| MCP tool registry, schemas, request handler |
| One handler per tool ( |
| Page, data, action, navigation, search business logic |
| WebSocket transport, wire types, captures |
| Session lifecycle, modal stack, reconnect |
| Claude Desktop Extension manifest |
| Builds |
| Builds + attaches |
| Deferred work (OAuth, 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 # 284 unit + protocol tests
npm run test:integration # 111 integration tests against real BC (requires running BC server)Roadmap
OAuth, 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 toolsbc_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.
| Name | Required | Description | Default |
|---|---|---|---|
| pageContextId | Yes | Page context ID returned by bc_open_page. Becomes invalid after closing. |
TDQS
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.
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.
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.
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.
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.
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).
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 }
Execute with staleness guard: { "pageContextId": "abc", "action": "Post", "expectedStateVersion": 5 }
| Name | Required | Description | Default |
|---|---|---|---|
| cue | No | Cue 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. | |
| action | No | Action caption name to execute (case-insensitive). Use action OR cue, not both. Must match a visible, enabled action from bc_open_page response. | |
| section | No | Section context. Required when using cue; optional for action. Examples: "lines", "subpage:Activities". | |
| bookmark | No | Stable row identifier for row-scoped actions. | |
| rowIndex | No | 0-based row position for row-scoped actions. | |
| pageContextId | Yes | Page context ID returned by bc_open_page. | |
| expectedStateVersion | No | Opt-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
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 details the invocation flow (validates action enabled, sends InvokeAction RPC, applies events), explains returned fields (updatedFields, changedSections, dialogsOpened, openedPages), and discloses the STALE_CONTEXT rejection semantics. It even covers confirmation dialogs and the follow-up bc_respond_dialog requirement. No contradiction with annotations (none present).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-structured with short thematic paragraphs. Every sentence carries meaningful information, but some redundancy exists (e.g., repeated mentions of action OR cue exclusivity across schema and text). It is appropriately sized for the tool's complexity, though it could be tightened slightly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (7 parameters, two modes, no output schema), the description is exceptionally complete. It covers prerequisites, row-scoping nuances, dialog handling, staleness checks, and return values. The examples illustrate typical use cases, making this a model description for a complex action executor.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description significantly enriches parameter meanings. It clarifies that section is required for cue but optional for action, distinguishes rowIndex vs bookmark for row-scoped actions, and explains the expectedStateVersion guard with a concrete error code. The multiple examples tie parameters to real scenarios, exceeding the schema's basic descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: "Executes either a named action OR a cue-tile drill-down on an open page." It clearly distinguishes the two modes and names sibling alternatives (bc_write_data, bc_navigate) in the "Do NOT use" section, leaving no ambiguity about scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: actions for header/line/system actions, cue for Role Center cue tiles. It also states exclusions: "Do NOT use this for writing field values -- use bc_write_data" and "Do NOT use this to open records from a list -- use bc_navigate with drill_down action instead." Additionally, it requires pageContextId from bc_open_page and explains when to pass section, rowIndex, bookmark, and expectedStateVersion.
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| field | Yes | Caption 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). | |
| search | No | Optional 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. | |
| maxRows | No | Maximum number of candidate rows to return. Defaults to 50. Max 500. BC may return fewer if the table has fewer records. | |
| pageContextId | Yes | Page context ID of the open page (card or list) returned by bc_open_page. |
TDQS
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.
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.
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.
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.
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.
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_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.
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": [...] }).
| Name | Required | Description | Default |
|---|---|---|---|
| pageId | Yes | Numeric BC page ID (e.g., 22 for Customer List, 21 for Customer Card). Use bc_search_pages to find IDs. | |
| bookmark | No | Open the page to a specific record. Bookmarks come from list row results in bc_open_page or bc_read_data. | |
| tenantId | No | BC tenant ID. Defaults to the server-configured tenant. Only needed in multi-tenant deployments. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully carries the transparency burden. It discloses return section shapes (card vs. list, header adaptation), enum options and selectedOption structure, stateVersion usage for stale-state protection, bookmark behavior, and explicit lifecycle instructions ('Always call bc_close_page when done', 'Do NOT call this if the page is already open').
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although lengthy, the description is well-structured with a clear opening statement, numbered workflow, bullet-like examples, and distinct topic paragraphs. Every sentence adds essential information about section shapes, enum handling, or tool relationships; there is no fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Without an output schema, the description thoroughly explains the return structure: sections, kinds, content shapes (fields, rows, actions), and totalRowCount. It provides multiple page-type examples (Card, List, Document) and covers enum and boolean field serialization, making the tool fully comprehensible in context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already covers all 3 parameters with descriptions, so the baseline is 3. The description adds extra value by giving concrete pageId examples (21, 22, 42), explaining that bookmarks come from prior list rows, and detailing the tenantId default behavior, thereby enriching schema semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with a clear, specific action: 'Opens a Business Central page by its numeric page ID and returns its complete state as a list of sections.' It further distinguishes itself from siblings by naming bc_query, bc_search_pages, and other tools that do not need a pageContextId, and by positioning itself as the entry point for page-scoped work.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance is provided: 'For bulk, read-only data over standard entities, prefer bc_query' and 'Use bc_search_pages first if you do not know the page ID.' The description also gives a typical workflow sequence and warns against reopening an already-open page, making usage expectations unambiguous.
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. Note: this env uses HTTP Basic auth (NavUserPassword); cloud/SaaS BC requires OAuth — set BC_ODATA_URL and configure OAuth externally for cloud envs.
| Name | Required | Description | Default |
|---|---|---|---|
| top | No | Maximum number of rows to return. Defaults to 100 if omitted to prevent accidental full-table scans. Pass explicitly to get more rows. | |
| entity | Yes | BC 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. | |
| expand | No | OData $expand for related entities. Examples: "salesLines", "customer($select=displayName)". Use sparingly — expanded entities increase response size significantly. | |
| filter | No | OData $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. | |
| select | No | Comma-separated OData $select field names to limit response size. Examples: "number,displayName,city", "id,amount,postingDate". Omit to return all fields. | |
| company | No | Override 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. | |
| orderby | No | OData $orderby expression. Examples: "displayName asc", "postingDate desc", "amount desc,number asc". |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and excels: it discloses that OData reads are read-only, top defaults to 100, queries are company-scoped automatically, and the special 'companies' entity exception. It also covers authentication requirements (Basic auth vs OAuth) and environment-specific details.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but exceptionally well-structured and front-loaded. It opens with the core purpose, then flows into usage guidelines, parameter guidance, and exclusions. Every sentence adds useful information, with clear section breaks and examples that earn their place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex tool with 7 parameters, no output schema, and OData semantics, the description is remarkably complete. It enumerates supported entities, provides filter/select/orderby syntax examples, explains the default top limit, covers the companies entity exception, and addresses authentication for different environments. Nothing important is left unexplained.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description adds substantial value by providing concrete OData examples for filter, select, orderby, and expand parameters, explaining defaults like top=100, and clarifying the behavior for company overriding. This goes well beyond the schema's basic parameter descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Reads records from Business Central in bulk using the Standard API v2.0' with a specific verb, resource, and scope. It distinguishes itself from sibling tools by explicitly comparing with bc_open_page + bc_read_data and highlighting that bulk reads are far more efficient.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description includes explicit 'When to use bc_query' and 'When NOT to use bc_query' sections, listing concrete use cases, alternatives, and exclusions. It names specific sibling tools (bc_open_page, bc_execute_action, bc_write_data) and clarifies when not to use bc_query for UI-driven flows or mutations.
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" }
| Name | Required | Description | Default |
|---|---|---|---|
| tab | No | Tab name to filter header fields by (e.g., "General", "Invoice Details", "Shipping and Billing"). Omit to return all header fields. | |
| sort | No | Sort 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. | |
| range | No | Slice a subset of repeater rows. Returns rows[offset..offset+limit]. Use with totalRowCount for pagination. | |
| columns | No | Column caption names to include in results. Omit to return all columns. Reduces output size. | |
| filters | No | Server-side filters to apply before reading. Multiple filters combine with AND logic. | |
| section | No | sectionId to refresh. Defaults to "header". Examples: "lines" (document line items), "factbox:Customer Statistics" (FactBox). Listed in the bc_open_page sections array. | |
| clearFilters | No | Clears 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. | |
| pageContextId | Yes | Page context ID returned by bc_open_page. |
TDQS
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.
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.
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.
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.
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.
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" }
| Name | Required | Description | Default |
|---|---|---|---|
| response | Yes | "ok" confirms, "cancel" dismisses, "yes"/"no" answers a question, "abort" force-closes, "close" closes a modal info page. | |
| dialogFormId | Yes | Dialog form ID from the dialogsOpened array returned by bc_execute_action or bc_write_data, or requestPage.formId returned by bc_run_report. | |
| pageContextId | Yes | Page context ID of the page that triggered the dialog. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden and excels. It explains the workflow (checking changedSections, handling chained dialogs), the meaning of each response value, and warns against guessing dialogFormId. No contradiction with any annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well-structured and front-loaded, with the first sentence stating the purpose. While lengthy, every sentence serves a purpose, including the example, and covers all critical aspects without unnecessary repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a workflow continuation tool, the description is complete: preconditions, ID sources, response choices, post-conditions, and chaining behavior are all covered. No output schema is needed given the explanatory detail.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although the schema has 100% description coverage, the description adds crucial operational context: where to obtain dialogFormId (from dialogsOpened/requestPage), how to choose the response, and a full example. This goes beyond the schema's basic definitions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function with a specific verb and resource: 'Responds to an open Business Central dialog or confirmation prompt.' It distinguishes itself from siblings like bc_execute_action and bc_write_data by focusing on the response step.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: triggered by dialogsOpened with requiresDialogResponse: true, or requestPage from bc_run_report. Also explicitly says when NOT to use: 'Do NOT call this without a preceding dialog.' It clearly identifies the triggering tools as alternatives.
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. Returns download.bytes (base64), download.contentType, and download.fileName. If the BC_REPORT_DIR env var is set the file is also saved to disk and savedPath is returned.
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" }
| Name | Required | Description | Default |
|---|---|---|---|
| format | No | Rendered 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. | |
| reportId | Yes | Numeric BC report ID to execute (e.g., 1306 for Customer Statement, 6 for Trial Balance). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and exceeds it. It discloses the internal BC 'Send to...' flow (opening format-selection dialog, SaveValue, OK, fetching from DynamicFileHandler.axd), request page mechanics, format availability caveats, env-var-dependent disk saving, and exact return fields (download.bytes, contentType, fileName, savedPath).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded: it starts with the core purpose, then breaks into clear sections for request page flow, format capture, availability, and usage boundaries. Examples are compact and illustrative. Despite length, every sentence carries operational value and the format aids scanning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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, the description is remarkably complete. It covers how to open a request page, how to fill it, how to capture formats, what is returned, format availability, common reports, and explicit exclusions. It leaves very little ambiguous for an agent selecting or invoking the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
While the schema already describes both parameters with 100% coverage, the description adds substantial meaning beyond that: the format param's optionality and its effect (omitting it opens the request page only), the 'pdf'/'excel'/'word' layout preferences, the reportId as a numeric ID with examples, and how parameters are filled via bc_write_data/request page context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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: 'Execute a Business Central report by its numeric report ID.' It clearly distinguishes reports from pages and explicitly lists when to use this tool vs. bc_open_page/bc_read_data, making sibling differentiation strong.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance ('Use this tool for reports that perform server-side actions... or to inspect and fill request page parameters') and when-not-to-use ('Do NOT use this for viewing data -- use bc_open_page and bc_read_data'). Also supplies common report examples like 1306 and 295, giving concrete usage context.
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..." }.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search term matching BC page names and keywords (e.g., "customer", "sales order", "chart of accounts"). Fuzzy matching supported. |
TDQS
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.
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.
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.
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.
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.
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 match. After switching, all subsequent bc_open_page, bc_read_data, bc_write_data, and bc_execute_action calls will operate against the new company's data.
Do NOT switch companies in the middle of a multi-step workflow (e.g., between creating a Sales Order and posting it). Complete all operations in the current company first, then switch.
Example: { "companyName": "CRONUS International Ltd." }
| Name | Required | Description | Default |
|---|---|---|---|
| companyName | Yes | Exact company name to switch to. Use bc_list_companies to see available company names. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully discloses critical side effects: all currently open pages become invalid and must be re-opened, and all subsequent data operations target the new company. It also notes the exact-match requirement for companyName, adding operational detail beyond the action itself.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is logically structured, starting with the core action, then side effects, prerequisites, and warnings. While longer than average, every sentence adds essential information for a state-changing tool, and the example makes parameter usage concrete.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the operation's purpose, side effects on open pages, impact on subsequent tools, prerequisite verification step, and a caution against misusing it mid-workflow. Given the lack of an output schema, it sufficiently informs the agent about what to expect, though it stops short of detailing return values or error handling.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents companyName as an exact match with guidance to use bc_list_companies, so the description adds minimal new parameter-specific value. The example and reiteration of exact match provide slight reinforcement, but the high schema coverage keeps the baseline at 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action (switch to a different company within the current Business Central session) and distinguishes it from sibling tools by specifying that subsequent bc_open_page, bc_read_data, etc. will operate against the new company. It also mentions invalidation of open pages, which sets it apart from navigation tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly instructs to use bc_list_companies first to verify the target company, and warns against switching mid-workflow, providing both positive and negative usage conditions. This gives clear when-to-use and when-not-to-use guidance.
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 }
| Name | Required | Description | Default |
|---|---|---|---|
| fields | Yes | Key-value pairs of field caption names and string values to write (e.g., { "Name": "Contoso", "City": "London" }). | |
| section | No | Section to write to (e.g., "lines" for document line items). Omit for header fields. | |
| bookmark | No | Stable row identifier from bc_read_data results. Preferred over rowIndex when rows may be reordered. | |
| rowIndex | No | 0-based row position in the repeater to write to. Use for line items. Prefer bookmark for stability. | |
| pageContextId | Yes | Page context ID returned by bc_open_page. | |
| expectedStateVersion | No | Opt-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
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.
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.
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.
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.
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.
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. Dates show when Glama detected each change.
14 tool updates
v1.4.0- First observed
bc_close_page - First observed
bc_execute_action - First observed
bc_list_companies - First observed
bc_lookup - First observed
bc_navigate - First observed
bc_open_page - First observed
bc_query - First observed
bc_read_data - First observed
bc_respond_dialog - First observed
bc_run_report - First observed
bc_search_pages - First observed
bc_switch_company - First observed
bc_wizard_navigate - First observed
bc_write_data
TDQS
Each tool targets a distinct operation—open, read, write, execute, close, navigate, dialog, report, search, lookup, query, company management, and wizard stepping—with explicit cross-references and 'do NOT use' guidance where overlap might otherwise occur (e.g., bc_navigate vs. bc_execute_action, bc_read_data vs. bc_query). The boundaries are sharp enough that agents can reliably select the correct tool.
The vast majority follow a consistent bc_verb_noun pattern (open_page, read_data, write_data, execute_action, close_page, run_report, respond_dialog, switch_company, list_companies). Minor deviations: bc_navigate, bc_lookup, and bc_query are single verbs, and bc_wizard_navigate reverses the noun_verb order; still, all are lowercase snake_case with the bc_ prefix, keeping the set predictable and readable.
14 tools is within the ideal 3–15 range and each one earns its place given the breadth of Business Central: page lifecycle, bulk query, reports, dialogs, wizard navigation, lookup, and company management. The count feels well-scoped rather than bloated or thin.
The page lifecycle is fully covered (open/read/write/execute/close) along with bulk OData reads, reports, lookup, dialog handling, navigation, and company switching. Minor gaps: codeunits are discoverable via bc_search_pages but not directly executable, and record creation relies on the UI 'New' action plus bc_write_data rather than a dedicated create tool—both are workarounds for agents.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP server unifying ERPs, CRMs, APIs and knowledge base for Claude, ChatGPT and Gemini.
- ZapierOAuthcom.zapier
Hosted MCP server connecting AI assistants to 9,000+ apps and 40,000+ actions via Zapier.
Let AI agents query data and act across all your business apps via MCP.
Cloud-hosted MCP server for secure AI access to enterprise data sources via CData Connect AI.
Related MCP Servers
- AlicenseAqualityDmaintenanceModel Context Protocol (MCP) server for Microsoft Dynamics 365 Business Central. Provides AI assistants with direct access to Business Central data through properly formatted API v2.0 calls.6308MIT
- AlicenseAqualityCmaintenanceMCP server for Microsoft Dynamics 365 Business Central that enables AI assistants to query and manage Business Central data via full CRUD operations.630MIT
- AlicenseAqualityCmaintenanceGives 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.1457MIT
- AlicenseBqualityCmaintenanceMCP server for Microsoft Dynamics 365 Business Central, enabling AI assistants to perform CRUD operations, query data, and retrieve schemas via Business Central API v2.0.630MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/keithdev21/Business-Central-Mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server