sap-b1-mcp
Provides read-only access to SAP Business One through the Service Layer REST API, enabling retrieval of business partners, items, open sales orders, customer balances, open invoices, stock by warehouse, and administrator-registered saved query results.
Click on "Deploy 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., "@sap-b1-mcpshow me overdue open invoices for customer C0001"
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.
sap-b1-mcp: read-only SAP Business One MCP server
An open-source Model Context Protocol server that lets AI assistants (Claude, ChatGPT via a bridge, Cursor, VS Code and other MCP clients) read data from one SAP Business One company through the Service Layer REST API.
It is a deliberately small, auditable starter:
Read-only by design. There are no tools that create, change or delete anything, and the HTTP client refuses any request other than
GET,Login,Logoutand running a pre-createdSQLQueriesentry.No free SQL and no free filters. Every tool has a typed input schema, a row limit and a field whitelist. Custom reports go through
run_saved_query, which only runs queries an administrator registered in a config file.Credentials stay private. They come from environment variables, are never logged, and are redacted from every error message.
Runs without SAP.
SAPB1_MOCK=1serves bundled demo fixtures, so you can try it and run the tests on any machine.
SAP and SAP Business One are trademarks or registered trademarks of SAP SE in Germany and other countries. This project is independent and is not affiliated with, sponsored by or endorsed by SAP SE.
Tools
Tool | What it returns | Key inputs | Row limit |
| One customer, supplier or lead by exact |
| 1 |
| Partners whose code or name contains text |
|
|
| One item master record by exact |
| 1 |
| Items whose code or name contains text |
|
|
| Open sales orders, newest first | optional |
|
| Account balance, open orders, open deliveries, credit limit and available credit |
| 1 |
| Open A/R invoices with unpaid amount and days overdue, plus a total | optional |
|
| In stock, committed, ordered and available per warehouse for one item |
| all warehouses of one item |
| Result of a query pre-registered by an administrator |
|
|
Every tool is annotated readOnlyHint: true, destructiveHint: false.
Field whitelists
The fields input only accepts names from these lists, so the model cannot pull other columns:
Business partners: CardCode, CardName, CardType, GroupCode, Phone1, EmailAddress, City, Country, Currency, FederalTaxID, Valid, Frozen
Balance: CardCode, CardName, Currency, CurrentAccountBalance, OpenOrdersBalance, OpenDeliveryNotesBalance, CreditLimit
Items: ItemCode, ItemName, ItemsGroupCode, BarCode, InventoryItem, SalesItem, PurchaseItem, InventoryUOM, QuantityOnStock, QuantityOrderedFromVendors, QuantityOrderedByCustomers, Valid, Frozen
Documents (orders, invoices): DocEntry, DocNum, CardCode, CardName, NumAtCard, DocDate, DocDueDate, DocTotal, DocCurrency, PaidToDate, DocumentStatus
Edit the lists in src/server.ts to fit your company.
Related MCP server: servicios-mcp
Quick start (no SAP needed)
npx -y @kodenova/sap-b1-mcp # with SAPB1_MOCK=1 in the environmentOr from source:
git clone https://github.com/kodenova/sap-b1-mcp.git
cd sap-b1-mcp
npm install
npm run build
npm test # runs in mock mode, no SAP needed
SAPB1_MOCK=1 node dist/index.js # stdio MCP server on demo dataRequires Node.js 18.17 or later.
Environment variables
Variable | Required | Description |
| yes | Service Layer base URL, e.g. |
| yes | Company database / schema name |
| yes | Dedicated read-only B1 user (see Security) |
| yes | Password of that user. Never logged, redacted from errors |
| no |
|
| no |
|
| no | Maximum rows any tool may return. Default 50, hard cap 500 |
| no | Per-request timeout. Default 30000 |
| no | Path to your saved-queries JSON (see below) |
| no |
|
| no |
|
See .env.example.
Client setup
Claude Desktop
Edit claude_desktop_config.json (Settings, Developer, Edit Config):
{
"mcpServers": {
"sap-b1": {
"command": "npx",
"args": ["-y", "@kodenova/sap-b1-mcp"],
"env": {
"SAPB1_SERVICE_LAYER_URL": "https://b1server:50000/b1s/v1",
"SAPB1_COMPANY_DB": "SBODEMOUS",
"SAPB1_USERNAME": "mcp_readonly",
"SAPB1_PASSWORD": "your-password",
"SAPB1_MAX_ROWS": "50"
}
}
}
}To try it without SAP, replace the env block with { "SAPB1_MOCK": "1" }. On Windows, if npx is not found, use "command": "cmd" and "args": ["/c", "npx", "-y", "@kodenova/sap-b1-mcp"].
Claude Code
claude mcp add sap-b1 \
-e SAPB1_SERVICE_LAYER_URL=https://b1server:50000/b1s/v1 \
-e SAPB1_COMPANY_DB=SBODEMOUS \
-e SAPB1_USERNAME=mcp_readonly \
-e SAPB1_PASSWORD=your-password \
-- npx -y @kodenova/sap-b1-mcpVS Code (GitHub Copilot agent mode)
.vscode/mcp.json, with the password prompted instead of stored:
{
"inputs": [
{ "type": "promptString", "id": "sapb1-password", "description": "SAP B1 password", "password": true }
],
"servers": {
"sap-b1": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@kodenova/sap-b1-mcp"],
"env": {
"SAPB1_SERVICE_LAYER_URL": "https://b1server:50000/b1s/v1",
"SAPB1_COMPANY_DB": "SBODEMOUS",
"SAPB1_USERNAME": "mcp_readonly",
"SAPB1_PASSWORD": "${input:sapb1-password}"
}
}
}
}Cursor, Windsurf and other stdio clients
Use the same command, args and env as the Claude Desktop example in the client's MCP config file (for Cursor: ~/.cursor/mcp.json or .cursor/mcp.json).
ChatGPT and other remote-only clients
ChatGPT connects to remote MCP servers over HTTPS; it does not launch local stdio processes. This package is a stdio server, so for ChatGPT you need to host it behind an MCP stdio-to-HTTP bridge on a server you control, with HTTPS and authentication in front of it. Do not expose an unauthenticated endpoint that holds your SAP credentials. If you want a hosted, authenticated HTTP version, see the footer.
Saved queries (run_saved_query)
The model cannot send SQL or OData filters. It can only pick a query by name from a JSON file you control and pass the typed parameters that query declares. Copy config/saved-queries.example.json to config/saved-queries.json and point SAPB1_SAVED_QUERIES_FILE at it.
Two kinds are supported:
{
"queries": {
"customers_over_balance": {
"kind": "odata",
"description": "Customers whose account balance is above a threshold",
"resource": "BusinessPartners",
"select": ["CardCode", "CardName", "CurrentAccountBalance"],
"filter": { "and": [
{ "field": "CardType", "op": "eq", "value": "cCustomer" },
{ "field": "CurrentAccountBalance", "op": "gt", "value": { "param": "minBalance" } }
]},
"orderBy": "CurrentAccountBalance desc",
"maxRows": 100,
"params": { "minBalance": { "type": "number", "default": 0 } }
},
"sales_by_month": {
"kind": "sqlquery",
"description": "Net A/R sales per month for a year",
"sqlCode": "KN_SALES_BY_MONTH",
"params": { "year": { "type": "integer", "required": true } }
}
}
}odataqueries run against a Service Layer entity set with a fixed$selectand a structured filter. Parameter values are type-checked (string,number,integer,date,boolean) and escaped; field names are validated.sqlqueryqueries run aSQLQueriesentry that your administrator already created in Service Layer (POST SQLQueries('code')/Listwith aParamList). Service Layer only acceptsSELECTstatements there. This server cannot create or changeSQLQueriesentries.
Security
No write path. The client-side guard (
assertReadOnlyinsrc/serviceLayer.ts) blocksPATCH,PUT,DELETEand everyPOSTexceptLogin,LogoutandSQLQueries('…')/List, before any request is sent. Tests cover this.Use a dedicated least-privilege B1 user. Create a separate user (for example
mcp_readonly) for this server. Do not usemanageror a superuser. In Administration → System Initialization → Authorizations → General Authorizations, set:Read Only for Business Partner Master Data, Item Master Data, Sales A/R (Orders, A/R Invoices) and Inventory reports you want exposed
No Authorization for everything else, including Administration, Banking, Financials and all purchasing or posting screens you do not need
The server is read-only on its own; the B1 authorizations are your second, independent layer. Service Layer logins need a suitable B1 user license, so check the licensing with your SAP partner.
Credentials. Read only from environment variables. Never written to logs. Password,
B1SESSIONandROUTEIDvalues are redacted from every error and log line. Prefer your client's secret prompt (as in the VS Code example) over storing the password in a config file.TLS. Certificate verification is on by default.
SAPB1_ALLOW_SELF_SIGNED=trueturns it off for the Service Layer host only and logs a warning. Plainhttp://is refused unlessSAPB1_ALLOW_HTTP=true.Bounded output. Every list tool has a row limit (default 20, max
SAPB1_MAX_ROWS, hard cap 500) and a field whitelist, so a prompt cannot dump whole tables.Prompt injection. Data returned from SAP (names, remarks) is passed to the model as data. Because the tools cannot write, a malicious record cannot make the model change your ERP through this server. Still, review what other tools you connect alongside it.
Logs go to stderr, never stdout (stdout is the MCP protocol channel).
Service Layer version notes
Built for the Service Layer v1 endpoint (
/b1s/v1), which uses OData v3-style JSON (odata.nextLink,{"error":{"message":{"value":…}}}). The client also reads the v2 (/b1s/v2, OData v4)@odata.nextLinkand error format, but filters with dates are written in v1 syntax, so v1 is the supported endpoint for 0.1.0.Paging: Service Layer returns 20 rows per page by default. The server sends
Prefer: odata.maxpagesize=Nand followsodata.nextLinkuntil the row limit is reached.Sessions: login is
POST /LoginwithCompanyDB,UserNameandPassword; theB1SESSION(andROUTEIDwhen load-balanced) cookies are reused. When the session times out (30 minutes by default), the server logs in again once and retries. It logs out on shutdown.contains()andstartswith()are used for search. On SAP HANA, string comparison is case-sensitive, sosearch_itemswithwidgetwill not matchWidget.SQLQueries(used bysqlquerysaved queries) exists only in newer Service Layer releases. Check thatSQLQueriesappears in your$metadatabefore using that kind.Field names follow the standard Service Layer metadata (
BusinessPartners,Items,Orders,Invoices). If your release or localization differs, adjust the whitelists insrc/server.ts.
Limitations
Read-only. No document creation, approvals, payments or master-data changes.
One company database per server process. Run several processes for several companies.
Service Layer only. The DI API (COM) and direct database access are not used.
No user-defined fields (UDFs) in the default whitelists. Add
U_…fields yourself if needed.Search uses
contains(), which can be slow on very large tables without suitable indexes; keep limits small.stdio transport only in this release. Remote clients such as ChatGPT need a bridge you host and secure.
Tested with unit tests, the bundled fixtures and a fake Service Layer HTTP server. Validate against a test company before you point it at production.
Development
npm install
npm run build
npm testProject layout:
src/index.ts stdio entry point (bin)
src/server.ts the 9 MCP tools, schemas, whitelists
src/serviceLayer.ts Service Layer client: login, cookies, paging, read-only guard
src/mock.ts fixture-backed data source for SAPB1_MOCK=1
src/savedQueries.ts saved-query config loader and parameter binding
src/odata.ts structured filter → OData, escaping
src/redact.ts credential redaction
fixtures/ demo data (fictional)
config/ saved-queries example
test/ vitest suites (mock mode + fake Service Layer)Contributions are welcome. Please keep the server read-only: pull requests that add write endpoints will not be merged into this package.
License
MIT © 2026 Kodenova / Zain Abbas. See LICENSE.
SAP and SAP Business One are trademarks or registered trademarks of SAP SE in Germany and other countries. This project is not affiliated with, sponsored by or endorsed by SAP SE. All demo data in fixtures/ is fictional.
Built by Kodenova, an independent SAP Business One development studio that works alongside your SAP partner. Need write access, custom tools or DI API? kodenova.com/services/mcp-server-development/
Available Tools
9 toolsget_business_partnerGet business partnerARead-onlyIdempotent
Fetch one business partner (customer, supplier or lead) by its exact CardCode.
| Name | Required | Description | Default |
|---|---|---|---|
| fields | No | Subset of fields to return. Allowed: CardCode, CardName, CardType, GroupCode, Phone1, EmailAddress, City, Country, Currency, FederalTaxID, Valid, Frozen | |
| cardCode | Yes | CardCode (exact code, case-sensitive on HANA) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, so the safety profile is covered. The description adds the entity-type scope but says nothing about not-found behavior, whether the response is a single object or empty, or the case-sensitivity caveat (that lives only in 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?
A single sentence with the resource and the lookup key front-loaded, and no filler. Nothing can be removed without losing 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?
For a simple two-parameter read with no output schema and full schema coverage, the definition gives enough to invoke it correctly. The only real gap is the absence of a not-found/response-shape note, which is minor for a straightforward lookup.
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% and both parameters are documented there, including the case-sensitive HANA note, so the schema carries the burden. The description reinforces that cardCode must be exact but never mentions the optional fields projection parameter.
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?
States a specific verb and resource ("Fetch one business partner") and narrows the domain with "(customer, supplier or lead)". The words "one" and "exact CardCode" implicitly separate it from the list-style sibling search_business_partners, but that sibling is never named.
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 phrase "by its exact CardCode" implies the precondition (you must already know the code) and hints that broad matching belongs elsewhere, but no alternative is named and no when-not condition is given. An agent must infer that search_business_partners is the fallback when the code is unknown or uncertain.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_customer_balanceGet customer balanceARead-onlyIdempotent
Account balance, open orders balance, open deliveries balance and credit limit for one business partner.
| Name | Required | Description | Default |
|---|---|---|---|
| cardCode | Yes | CardCode (exact code, case-sensitive on HANA) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so safety is covered. The description adds genuine value by disclosing the payload contents (four balance figures plus credit limit), which matters since there is no output schema. It says nothing about behavior for unknown card codes, empty results, or whether balances are real-time.
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?
A single tight sentence that front-loads the returned metrics with zero filler. It is a noun fragment rather than a full sentence, which is minor, but every word carries 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?
For a one-parameter read tool with full schema coverage and rich annotations, the description is nearly sufficient; enumerating the returned fields compensates for the absent output schema. Missing are edge-case behavior and result-shape hints (e.g. whether all four figures are always present).
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 the single parameter is fully documented, including case sensitivity on HANA. The description's phrase 'for one business partner' reinforces that cardCode identifies exactly one partner rather than filtering a list, but adds no format or lookup guidance beyond the schema. Baseline 3 applies.
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 names the specific resource (business partner) and enumerates the four values returned: account balance, open orders balance, open deliveries balance and credit limit. Combined with the tool name the verb is unambiguous. It does not explicitly contrast with get_business_partner, so it falls short of sibling differentiation.
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?
Usage is only implied: it fetches figures for exactly one business partner, which tells an agent this is a single-record lookup rather than a search. There is no statement of when to prefer get_business_partner or get_open_invoices, and no preconditions such as the card code needing to exist.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_itemGet itemARead-onlyIdempotent
Fetch one item master record by its exact ItemCode.
| Name | Required | Description | Default |
|---|---|---|---|
| fields | No | Subset of fields to return. Allowed: ItemCode, ItemName, ItemsGroupCode, BarCode, InventoryItem, SalesItem, PurchaseItem, InventoryUOM, QuantityOnStock, QuantityOrderedFromVendors, QuantityOrderedByCustomers, Valid, Frozen | |
| itemCode | Yes | ItemCode (exact code, case-sensitive on HANA) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, and openWorldHint=true, so the safety profile is covered externally. The description contributes the exact-match/case-sensitivity framing but says nothing about not-found behavior or whether missing items return empty vs. error.
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?
A single front-loaded sentence with no filler; the key constraint (exact code) is placed where it is read first.
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 two-parameter, fully documented read tool with rich annotations and no output schema, this is close to sufficient. The only meaningful omission is behavior on a non-existent ItemCode and whether 'fields' projection affects payload size.
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%, including the case-sensitivity note on HANA and the full enum list for 'fields'. The description's mention of 'exact ItemCode' restates the schema rather than adding format, casing, or projection guidance.
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?
Names a specific verb (Fetch) and resource (one item master record) with the lookup key (exact ItemCode), so an agent knows this is a single-record exact-match read. It does not explicitly name search_items as the alternative for fuzzy/list lookups, so the sibling distinction is only implied by 'one' and 'exact'.
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 phrase 'by its exact ItemCode' implies the precondition for use (you must already know the code), which is meaningful guidance. But it never states when to prefer search_items instead, nor what happens when the code does not exist.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_open_invoicesGet open A/R invoicesARead-onlyIdempotent
Open A/R invoices with the unpaid balance (DocTotal - PaidToDate), optionally for one customer or only overdue ones.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max rows to return (1-50, default 20) | |
| cardCode | No | CardCode (exact code, case-sensitive on HANA) | |
| overdueOnly | No | Only invoices with DocDueDate before today |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint, so safety is covered. The description adds real value by spelling out the unpaid-balance formula, but says nothing about ordering, pagination behavior, or what happens when no filters are supplied.
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?
A single tight sentence that front-loads the resource and immediately defines the balance calculation, with no filler. It is appropriately sized for a simple read tool, though the parenthetical formula slightly interrupts the flow.
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 three-parameter read-only listing tool with a fully described schema and no output schema, the definition covers what the tool returns (invoices plus unpaid balance) and the optional narrowing options. Only minor gaps remain, such as default sort order or maximum result behavior.
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 all three parameters are already documented, including limits and the HANA case-sensitivity note. The description only loosely restates customer and overdue filtering and adds no format or edge-case detail beyond the schema, so the baseline of 3 applies.
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?
Names a specific resource (open A/R invoices) and defines the key computed field as DocTotal - PaidToDate, which tells an agent exactly what it will get back. It is less explicit about how it differs from close siblings like get_customer_balance or get_open_sales_orders, so it stops short of a 5.
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 phrase 'optionally for one customer or only overdue ones' hints at when the optional parameters apply, but there is no explicit when-to-use or when-not-to-use guidance and no mention of the sibling tools (e.g. get_customer_balance, get_open_sales_orders) that an agent might otherwise pick.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_open_sales_ordersGet open sales ordersARead-onlyIdempotent
Open (not closed, not cancelled) sales orders, optionally for one customer, newest first.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max rows to return (1-50, default 20) | |
| fields | No | Subset of fields to return. Allowed: DocEntry, DocNum, CardCode, CardName, NumAtCard, DocDate, DocDueDate, DocTotal, DocCurrency, PaidToDate, DocumentStatus | |
| cardCode | No | CardCode (exact code, case-sensitive on HANA) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare the operation as read-only, idempotent, open-world, and non-destructive. The description adds meaningful context by defining 'open' as not closed and not cancelled, and by stating the default ordering is newest first.
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 a single compact sentence with no wasted words. The key scope and ordering information is front-loaded and easy to parse.
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 read-only list tool with rich annotations and complete schema documentation, the description covers the essential scope and behavior. It could say slightly more about result shape or pagination expectations, but nothing critical 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 description coverage is 100%, so limit, fields, and cardCode are already fully documented in the schema. The description reinforces that customer filtering is optional but does not add syntax, edge cases, or other meaning beyond the 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 resource ('sales orders') and scope ('open, not closed, not cancelled'), which distinguishes it from read-only invoice tools. It does not explicitly name sibling alternatives, but the resource and status are specific enough for an agent to identify the operation.
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 implies usage by saying the results can be optionally filtered for one customer and are returned newest first. However, it does not state when to use this tool instead of siblings like get_open_invoices or run_saved_query, nor does it give exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_stock_by_warehouseGet stock by warehouseBRead-onlyIdempotent
In-stock, committed, ordered and available quantity per warehouse for one item (from ItemWarehouseInfoCollection).
| Name | Required | Description | Default |
|---|---|---|---|
| itemCode | Yes | ItemCode (exact code, case-sensitive on HANA) | |
| includeEmpty | No | Include warehouses with zero stock (default false) | |
| warehouseCode | No | Only this warehouse (WhsCode) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and non-destructive behavior, so the safety profile is covered structurally. The description adds genuine context by naming the underlying collection (ItemWarehouseInfoCollection), which hints at data provenance and result shape, but says nothing about error behavior for unknown item codes or how empty warehouses are handled at the response level.
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?
A single dense sentence with no filler, and the resource is front-loaded ahead of the qualifier ('from ItemWarehouseInfoCollection'). Slightly awkward phrasing but zero waste.
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?
With no output schema, the description carries the return-value burden and does so by listing the four quantity measures, which is the key thing an agent needs. Missing only edge-case notes (unknown item, empty result set) for a lookup tool that is otherwise straightforward.
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 itemCode, includeEmpty and warehouseCode are already documented in the schema. The description only restates the 'one item / per warehouse' scoping, adding no format, case-sensitivity or default details beyond what the schema provides. Baseline 3 applies.
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 names the exact resource (per-warehouse stock quantities for a single item) and enumerates the returned measures (in-stock, committed, ordered, available), so an agent can distinguish it from get_item. It doesn't explicitly name a sibling it is not, so it stops short of a 5.
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?
There is no when-to-use guidance and no routing to alternatives such as get_item or search_items when the agent has a partial code. Usage is only implied by 'for one item', leaving the agent to infer when this tool beats the broader item lookup.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_saved_queryRun saved queryARead-onlyIdempotent
Run a query that an administrator pre-registered in the saved-queries config file. No free SQL or free filters are accepted. Available: customers_over_balance: Customers whose account balance is above a threshold, highest first; invoices_due_before: Open A/R invoices due before a date; sales_by_month: Net A/R sales per month for a year (needs SQLQueries entry KN_SALES_BY_MONTH created by your admin)
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Saved query name | |
| limit | No | Max rows to return (1-50, default 20) | |
| params | No | Parameters declared by that saved query |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, non-destructive, open-world behavior, so the bar is lower. The description adds genuinely new behavioral context: the query set is fixed by an administrator in a config file, arbitrary SQL/filters are rejected, and sales_by_month additionally requires a KN_SALES_BY_MONTH entry to exist. It does not say what happens when an unregistered name or undeclared param is supplied.
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?
Front-loaded with the core constraint (pre-registered only, no free SQL) before the catalog, and every clause carries information. The dense single-sentence catalog is slightly harder to scan than a list would be, but nothing is redundant.
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?
There is no output schema, and the description does not describe the shape of returned rows, though "highest first" hints at ordering for one query. For a tool with a nested free-form params object, the missing param key names and unspecified behavior on an invalid name leave real gaps for correct 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?
Schema coverage is 100%, so baseline is 3, but the description meaningfully upgrades the schema: the schema types "name" as a bare string with no enum, and the description supplies the closed list of three valid values. It also clarifies that "params" must match the declaration of the chosen query rather than being free-form, though it never states the actual param key names (threshold, date, year) an agent must pass.
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?
States a specific verb and resource ("Run a query that an administrator pre-registered") and then enumerates the exact closed set of allowed query names with a one-line meaning for each. This immediately separates it from the sibling tools, which are direct entity getters rather than pre-registered, parameterized queries.
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?
"No free SQL or free filters are accepted" plus the admin-config prerequisite gives a clear condition for use and rules out ad-hoc filtering attempts. It stops short of naming a sibling to fall back on (e.g., use get_open_invoices for a plain listing), so the routing guidance is one-sided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_business_partnersSearch business partnersARead-onlyIdempotent
Search business partners whose CardCode or CardName contains the text. Optionally filter by type.
| Name | Required | Description | Default |
|---|---|---|---|
| type | No | ||
| limit | No | Max rows to return (1-50, default 20) | |
| query | No | Text contained in CardCode or CardName | |
| fields | No | Subset of fields to return. Allowed: CardCode, CardName, CardType, GroupCode, Phone1, EmailAddress, City, Country, Currency, FederalTaxID, Valid, Frozen |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint, so safety is covered without the description's help. The description adds real value in disclosing the matching rule (substring contained in CardCode OR CardName), which is behavior beyond the annotations. It does not disclose result ordering, case sensitivity, or whether inactive/frozen partners are included, which matter for a search tool.
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?
Two short sentences, front-loaded with the primary matching rule and followed by the optional refinement — no filler. It is perhaps too terse for a tool with four parameters, but nothing is wasted or buried.
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 tool is simple and fully read-only, and annotations plus the schema cover the safety profile and most parameters, so the bar is modest. Still missing for correct invocation: behavior with an empty or omitted query (return everything capped at limit?), case sensitivity, and result ordering — all relevant to a free-text search over a large partner master.
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 75% and the description reinforces the query semantics ('contains') and defines the purpose of the type filter, whose enum values carry no description in the schema. It adds little for limit and fields, but those are already self-describing in the schema, so the description complements rather than duplicates.
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?
States a specific verb and resource ('Search business partners') plus the exact match semantics (substring on CardCode or CardName) and an optional type filter. This implicitly distinguishes it from the sibling get_business_partner (exact single-record lookup), but it never names the sibling or draws the contrast explicitly.
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?
Usage is only implied: an agent can infer this is the tool for partial-text/exact-prefix lookup, and the 'optionally filter by type' clause hints at narrowing. There is no guidance on when to prefer get_business_partner, what happens with an empty query, or how to page beyond the limit, so the agent must infer the boundary conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_itemsSearch itemsARead-onlyIdempotent
Search items whose ItemCode or ItemName contains the text.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max rows to return (1-50, default 20) | |
| query | No | Text contained in ItemCode or ItemName | |
| fields | No | Subset of fields to return. Allowed: ItemCode, ItemName, ItemsGroupCode, BarCode, InventoryItem, SalesItem, PurchaseItem, InventoryUOM, QuantityOnStock, QuantityOrderedFromVendors, QuantityOrderedByCustomers, Valid, Frozen | |
| inStockOnly | No | Only items with QuantityOnStock > 0 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint, idempotentHint, non-destructive and openWorld, so the safety profile is covered. The description adds the matching semantics (containment on ItemCode or ItemName) beyond the annotations, which is useful, but says nothing about case sensitivity, result ordering, or what happens when the limit truncates a larger result set.
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?
A single sentence with no filler, and the most important information (what is searched, and on which fields) is front-loaded. Nothing in it could be removed without losing meaning.
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?
With four optional parameters, full schema coverage and no output schema, the description is close to sufficient, but it leaves real ambiguity: whether matching is case-sensitive or tokenized, whether results are ordered, and how the default limit of 20 interacts with a broad query. For a search tool these are the details an agent most needs.
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 limit, query, fields and inStockOnly are all documented in the schema itself. The description restates the query semantics that the schema already gives and adds nothing about the other three parameters; baseline 3 is appropriate when the schema does the heavy lifting.
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 names a specific verb ("Search") and resource ("items") and states the matching rule: substring match on ItemCode or ItemName. That is enough to distinguish it from get_item (key lookup) by implication, but it never names the sibling explicitly, so the differentiation is inferred rather than stated.
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?
Usage is only implied: the agent must infer that this tool is for partial-text lookup while get_item is for exact retrieval. There is no explicit when-to-use guidance, no mention of when not to use it, and no reference to search_business_partners as the analogous tool for a different resource.
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.
9 tool updates
v0.1.0- First observed
get_business_partner - First observed
get_customer_balance - First observed
get_item - First observed
get_open_invoices - First observed
get_open_sales_orders - First observed
get_stock_by_warehouse - First observed
run_saved_query - First observed
search_business_partners - First observed
search_items
TDQS
Scored across 9 tools
The get_by_exact_key vs search_by_text split is clean and consistent across business partners and items, and the transactional tools (open orders, open invoices, stock by warehouse) target distinct resources. The only fuzziness is between run_saved_query's pre-built queries (customers_over_balance, invoices_due_before) and the single-record tools get_customer_balance/get_open_invoices, which an agent could pick wrongly, though the descriptions differentiate them.
All names are snake_case with a leading verb (get_, search_, run_), and the pattern maps predictably to intent: get_ = exact-key fetch, search_ = text lookup, get_open_* = filtered document lists. No mixed conventions or vague verbs.
Nine tools is well within the sweet spot and each covers a distinct read pattern (master data, balances, stock, open documents, canned reports). Nothing looks padded or redundant.
The server is coherently read-only, but the document surface stops at list level: there is no tool to fetch a single sales order or invoice with its line details, no price-list/item-warehouse-adjacent lookups beyond the one stock call, and no purchase-side coverage. run_saved_query is an admin-gated escape hatch rather than a general remedy, so agents will hit dead ends when drilling into a document.
Maintenance
Related MCP Connectors
Odoo MCP connector for ChatGPT and Claude: ask sales, invoices and stock in plain words. Read-only.
The HubSpot MCP Server acts as a bridge that enables AI assistants and Large Language Models to securely interact with HubSpot CRM data through natural conversation, without requiring users to understand complex API structures. It provides read-only access to standard CRM objects (contacts, companies, deals, tickets, products, invoices, and more) and their associations, secured via OAuth 2.0, allowing AI agents to perform tasks like summarizing deals, fetching company updates, and looking up record changes.
One governed MCP endpoint for 40+ business systems. Read-only, source-cited answers.
Query your org's data in natural language — read-only MCP access to SQL, NoSQL, files & warehouses.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables interaction with SAP Business One via Service Layer REST API to retrieve and create business data such as partners, orders, invoices, items, and stock levels through natural language.1-
- AlicenseNot gradedqualityBmaintenanceRead-only MCP server that exposes SAP Business One Service Layer and Cisco CCWR contract search APIs for querying sales orders, delivery notes, business partners, and service contracts, without modifying data.MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants like Claude, ChatGPT, and Copilot to access SAP Business One, S/4HANA Cloud, and Concur data through MCP tools for partners, orders, invoices, expense reports, and more.AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceConnects SAP Business One to Claude, ChatGPT, and Copilot, providing 12 MCP tools to read business partners, items, sales orders, invoices, quotations, and delivery notes, and to create sales orders.AGPL 3.0