Business Central MCP Server
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 MCP Servershow me open sales orders"
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: Dynamics 365 Business Central Admin 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?
No annotations provided, so description carries full burden. It discloses that pageContextId becomes invalid, that it's safe after errors, and that it frees server resources. Could mention idempotency but still strong.
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?
Description is three paragraphs and front-loaded with key action. Every sentence adds value, though slightly verbose. Could be tightened without losing content.
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 simple close tool with one parameter and no output schema or annotations, the description is comprehensive. Covers purpose, usage, behavioral implications, and 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?
Schema coverage is 100% and schema already describes pageContextId. Description adds that it requires context from bc_open_page and becomes invalid, but this is marginal. 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 'Closes an open Business Central page and frees its server-side resources', providing a specific verb (close) and resource (page). It effectively distinguishes from sibling tools like bc_open_page, bc_read_data, etc.
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 'Always call this when you are finished working with a page' and warns 'Do NOT call this in the middle of a multi-step workflow'. It also provides alternatives: 'use bc_read_data to refresh data instead' for resetting.
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?
No annotations provided, but the description fully discloses behavior: validates action is enabled, sends RPC, applies events, returns updatedFields/changedSections/dialogsOpened/openedPages, fails with STALE_CONTEXT if state version mismatches, and describes dialog handling requiring bc_respond_dialog.
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: starts with a succinct summary, then details each mode, includes constraints, and ends with clear examples. Every sentence adds value 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?
Given the tool's complexity (7 parameters, two modes, staleness guard, dialog handling), the description covers all prerequisites, behavior, and follow-ups. Examples cover common use cases. No gaps.
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?
Schema coverage is 100%, but the description adds critical semantics: mutual exclusivity of action/cue, section requirement for cue, rowIndex/bookmark for row-scoped actions, and expectedStateVersion guard. Examples illustrate usage.
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 executes a named action or a cue-tile drill-down on an open page. It distinguishes itself from siblings by explicitly warning not to use for writing fields (bc_write_data) or opening records from a list (bc_navigate).
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 (execute actions, drill-down cues), when-not-to-use (writing fields, opening records), and alternatives (bc_write_data, bc_navigate). Also explains staleness guard and mutual exclusivity of action/cue.
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?
Details internal behavior (opens BC Companies system page, reads entries, closes it) and explicitly states no effect on currently open pages or session state. With no annotations, the description fully discloses behavioral traits.
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?
Every sentence is purposeful. The description is front-loaded with the core purpose, followed by usage guidance and behavioral notes. No redundancy or filler.
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 zero parameters and no output schema, the description covers all necessary context: purpose, usage, internal behavior, side effects, and relationship to sibling tools. Nothing is missing.
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?
Schema has no parameters, and description explicitly confirms 'No parameters are required.' This adds clarity beyond the empty schema, especially by explaining the absence.
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?
Clearly states 'List all companies available' with specific verb and resource. Distinguishes itself from sibling bc_switch_company by indicating its role as a discovery/verification step before switching.
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 not to use if company name is already known. Provides a recommended workflow: bc_switch_company then bc_open_page for working with data.
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?
No annotations provided, but description fully discloses non-mutating behavior, that it always cancels without selecting, and mentions error conditions for lookupCustom. Clearly defines side effects.
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 with purpose first, then usage, exclusions, and workflow. Slightly lengthy but each sentence is informative. No 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 no output schema, description explains return of candidate rows and error handling. Covers prerequisites (open page), limits (maxRows), and search filtering. Complete for the tool's complexity.
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?
Schema coverage is 100%, baseline 3. Description adds examples ('Salesperson Code'), clarifies search string usage, and states defaults for maxRows. Adds value beyond schema.
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 enumerates candidate values for FK fields via BC's Lookup. It distinguishes from siblings like bc_open_page and bc_write_data.
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 states when to use (before writing, field has isLookup=true) and when not to use (option fields, lookupCustom=true, isLookup false/absent). Provides example workflow.
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?
No annotations provided, so description fully carries behavioral disclosure. Details return structure (sections, kinds, content shapes, header adaptation), extra properties for option/enum fields, stateVersion for stale state guarding, and the requirement to call bc_close_page. 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Relatively long but well-organized: begins with purpose and return structure, then workflow, parameter details, and examples. Every sentence adds value; front-loads key behavioral and usage info. Minor redundancy in examples but overall efficient for the complexity.
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?
No output schema, but description thoroughly explains return structure with examples and edge cases (card vs list pages, option/enum fields). Covers all parameters, workflow, sibling distinctions, and practical usage notes. Complete for an AI agent to use 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?
Schema coverage is 100%, baseline 3. Description adds meaning: explains bookmark origin from list rows, pageId numeric range and discovery via bc_search_pages, tenantId defaulting. Provides context beyond schema 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 it 'Opens a Business Central page by its numeric page ID and returns its complete state as a list of sections.' It distinguishes from siblings by naming tools that do not need a pageContextId, such as bc_query and bc_search_pages. Examples further clarify the tool's role.
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 workflow (bc_open_page -> bc_read_data -> ... -> bc_close_page). States when to avoid calling this tool (if page already open, reuse contextId) and recommends alternatives: prefer bc_query for bulk read-only, use bc_search_pages first if page ID unknown. Clearly distinguishes sibling tools that don't need pageContextId.
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, the description carries full burden. It discloses read-only nature (OData reads), company scoping, default top to prevent full-table scans, auth method (HTTP Basic), and special handling of 'companies' entity. However, it does not describe the response format (e.g., array of objects) or pagination behavior, which would be 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 well-structured with clear sections but is somewhat verbose, including repeated entity lists and extended examples. It could be more concise without losing 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 7 parameters and no output schema, the description is highly complete, covering purpose, usage, parameter syntax, alternatives, auth, and edge cases. It lacks only the response format/pagination details, which slightly reduces completeness.
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?
Schema description coverage is 100%, so baseline is 3. The description adds significant value beyond schema by providing OData syntax examples for filter, select, orderby, expand, and explaining the default top behavior. It also lists valid entity names and clarifies company scope.
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 reads records in bulk via BC Standard API v2.0, specifying a specific verb ('reads') and resource ('records from Business Central'). It distinguishes from sibling tools like bc_open_page and bc_read_data by emphasizing server-side filtering and projection.
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' sections, listing appropriate use cases (e.g., structured data retrieval, bulk reads with server-side operations) and inappropriate ones (e.g., UI-driven flows, writing). It names alternative tools like bc_open_page and bc_execute_action.
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?
No annotations provided, so description carries full burden. It details return shape, stateVersion usage, filtering/sorting/column selection behavior, clearFilters limitations (page-defined filters remain), and enum handling. Lacks explicit statement that tool is read-only, but context implies no side effects.
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?
Long but well-structured: paragraphs for each feature, front-loaded with purpose and return value. Each section adds value; could slightly reduce redundancy but appropriate for complexity.
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?
No output schema, but description explains return format (section object with fields, rows, totalRowCount, stateVersion). Covers all 8 parameters, including nested objects. Provides edge cases (clearFilters not fully clearing), pagination guidance, and multiple examples. Extremely complete for a complex 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?
Schema coverage is 100%, providing baseline 3. Description adds extensive context: filter syntax examples, sorting constraints (non-sortable columns), range slicing for pagination, clearFilters stacking behavior, and tab filtering. Examples illustrate each parameter's use.
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 refreshes a single section on an already-open page. It distinguishes from siblings like bc_query by explicitly saying when NOT to use it, and ties it to a prerequisite (bc_open_page).
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: 'Do NOT use this for bulk or analytical reads... prefer bc_query' and 'Use bc_read_data when you need the interactive page's exact rows, factboxes, or option metadata.' Also provides context on when to use different sections.
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?
No annotations were provided, so the description bears full responsibility. It explains the effects of different response values, mentions checking changedSections, and warns about chained dialogs. However, it does not mention any rate limits, authentication requirements, or potential irreversibility of actions beyond the dialog context.
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 with the primary purpose upfront, followed by usage details, parameter guidance, and examples. It is somewhat long but each section adds necessary context for correct usage. Could be slightly more concise, but maintains 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 complexity of dialog handling in Business Central, the description covers all essential aspects: when to call, how to obtain identifiers, valid responses, what to check after calling (changedSections, chained dialogs). With no output schema, the description adequately explains expected outcomes.
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?
Schema description coverage is 100%, so baseline is 3. The description adds value by contextualizing parameters: where to find dialogFormId (from dialogsOpened or requestPage), what each response value means in context (e.g., 'yes' for yes/no questions), and example usage. This enhances the schematic 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 responds to Business Central dialogs/confirmation prompts, specifying that dialogs are triggered by other tools (bc_execute_action, bc_write_data, bc_run_report). This distinguishes it from sibling tools like bc_open_page or bc_read_data which have different purposes.
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-call guidance: only after receiving a dialogsOpened array or requestPage, and warns not to call without a preceding dialog. It also advises to use the exact dialogFormId from responses and gives examples of response values like 'ok' for confirmation dialogs.
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?
Without annotations, the description fully details behavior: how format capture works (internal 'Send to...' flow, SaveValue, confirmation), that format availability depends on layouts, error behavior (returns available options), and that output includes download fields and optional disk saving. No contradictions since annotations are absent.
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 with clear sections and examples, but it is lengthy. Every part adds value, so it earns a high score, yet could be slightly more concise without losing 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 complexity (request page handling, internal format capture), the description covers all essential aspects: parameters, usage steps, return values (download.bytes, contentType, fileName, savedPath), error behavior, and references to sibling tools. No output schema, but return info is adequately described.
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?
Schema coverage is 100%, but description adds significant context: explains format enum values (pdf is BC default, excel prefers 'data only', word targets any option containing 'Word'), clarifies that omitting format opens request page only, and provides example report IDs. This goes well beyond the schema 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 'Execute a Business Central report by its numeric report ID.' It distinguishes reports from pages, listing specific use cases like batch posting and data processing, and provides examples for different modes (request page only vs. capturing PDF/Excel/Word). This matches the sibling context where bc_open_page and bc_read_data are for data retrieval.
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 states when to use (server-side actions, request page inspection) and when not to use ('Do NOT use this for viewing data – use bc_open_page and bc_read_data'). Provides alternatives and explains the request page flow (bc_write_data, bc_respond_dialog) and format flow. Also lists common report IDs for reference.
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?
Thoroughly discloses profile-scoped behavior and empty-result remediation. Explains that runTarget is AL name, not numeric ID, and that caller needs to map to page ID. Describes return format and note field.
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 with clear sections, examples, and edge-case handling. Slightly lengthy but every sentence adds value.
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 single parameter, no output schema, and sibling tools, description is exhaustive: covers return format, profile scoping, runTarget nuance, empty behavior, and examples.
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?
Schema coverage is 100% with description of 'query' parameter. Description adds context: fuzzy matching, keywords, and examples, going beyond schema.
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?
Clearly states it searches BC's Tell Me index for pages, reports, codeunits, etc. Returns specific fields. Distinguishes from sibling bc_open_page (use when page ID 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?
Explicitly says when to use (don't know page ID) and when not (already know numeric ID). Provides alternative tool bc_open_page. Also warns not to use for reading data.
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?
Discloses that all pages are invalidated, pageContextIds become unusable, and subsequent calls affect the new company. No annotations provided, so description fully covers behavioral traits.
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 with main purpose first, followed by detailed behavior, usage warnings, and example. No redundant 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 no output schema or annotations, description covers all necessary context: prerequisites, side effects, workflow implications, and example usage.
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?
Schema covers 100% of parameters with description, and description adds example, exact match requirement, and reference to bc_list_companies, enhancing clarity beyond schema.
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?
Description clearly states 'Switch to a different company' and specifies that it invalidates pages, distinguishing it from sibling tools like bc_open_page or bc_navigate.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit guidance: use bc_list_companies first, avoid switching mid-workflow, and exact match required. Clearly states when to use and when not to.
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 full burden. It discloses key behaviors: server confirmation may differ from input due to formatting/auto-completion/lookups, read-only fields cause errors, validation cascades, and expectedStateVersion rejection with STALE_CONTEXT. No contradictions with annotations (none provided).
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 with clear paragraphs and bulleted examples. Each sentence adds value, though slightly verbose; could be trimmed without losing meaning. Front-loaded with core purpose.
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 6 parameters, nested objects, and no output schema, the description is thorough. However, it does not fully specify the return value structure beyond 'server-confirmed value', and examples omit return shapes. A minor gap for a complex write 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?
All 6 parameters have schema descriptions (100% coverage), and the description adds substantial value: explains fields structure (caption-name keys, string values), section usage, bookmark vs rowIndex semantics, and expectedStateVersion as staleness guard with concrete examples. Goes well beyond schema.
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?
Explicitly states 'writes one or more field values on an already-open Business Central page', clearly identifying the action and resource. Distinguishes from siblings like bc_execute_action and bc_navigate by explicitly stating what not to use it for.
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 comprehensive when-to-use and when-not-to-use guidance, including explicit alternatives (e.g., 'Do NOT use this for triggering actions like Post, Delete, or Release -- use bc_execute_action instead'). Also advises on related fields grouping, bookmark vs rowIndex preference, and staleness guard usage.
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 has a clearly distinct purpose: opening pages, navigating, reading/writing data, executing actions, closing pages, searching, responding to dialogs, switching companies, listing companies, running reports, driving wizards, looking up FK values, and bulk querying. There is no ambiguity or overlap.
All tools follow the 'bc_' prefix with lowercase and underscores. Most use a verb_noun pattern (e.g., 'bc_open_page', 'bc_read_data'), and even those with just a verb (e.g., 'bc_navigate', 'bc_lookup') maintain consistency. No mixed conventions.
14 tools is well within the ideal 3-15 range for a complex ERP system like Business Central. Each tool serves a specific and necessary function, covering page management, data operations, actions, search, reporting, wizards, lookups, and bulk queries.
The tool set covers the full lifecycle of interacting with Business Central: opening, navigating, reading, writing, executing actions, handling dialogs, closing pages, plus searching, company management, report execution, wizard driving, lookups, and bulk OData queries. No obvious gaps for typical agent workflows.
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
Official Akeda ERP connector for tasks, CRM, chats, documents, finance and operations.
Let AI agents query data and act across all your business apps via MCP.
Run WhatsApp Business campaigns from any AI assistant: contacts, segments, and broadcasts.
Connect your business apps to your MCP compatible AI in 1 click
Related MCP Servers
- AlicenseAqualityAmaintenanceGive AI assistants direct access to Microsoft Dynamics 365 Business Central via native WebSocket protocol.145738MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to manage Dynamics 365 Business Central environments through natural language commands, including environment, app, session, and extension management.189MIT
- FlicenseNot gradedqualityBmaintenanceConnects AI clients like VS Code Copilot and Claude Desktop to Microsoft Dynamics 365 Business Central via the Cloud Events API.-
- 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
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/MehrozeKhan73/Business-Central-MCP-Integration'
If you have feedback or need assistance with the MCP directory API, please join our Discord server