Skip to main content
Glama
leonardows1

SAP B1 ServiceLayer MCP Server

by leonardows1

SAP B1 ServiceLayer MCP Server

MCP (Model Context Protocol) server to connect AI assistants (opencode, Claude, etc.) to the SAP Business One 10.0 ServiceLayer, on a local network. Runnable with npx from this GitHub repository, without installing anything on the PC.

Features

  • Read-only by default: with SAP_B1_READONLY=true (default) only query tools (GET) are registered. Write tools (POST/PATCH/DELETE) do not exist on the server and cannot be called.

  • Full discovery: sap_list_entities, sap_get_entity_schema and sap_list_actions query GET /$metadata (downloaded once per process and cached) and expose the ~140 CRUD entities (including user tables @ and UDOs) and the hundreds of ServiceLayer service methods.

  • Optional write mode: with SAP_B1_READONLY=false, sap_create, sap_update, sap_delete are enabled for ServiceLayer entities and sap_call_action for service methods (which may have side effects).

  • Run via npx github:: no manual installation.

  • Managed session: implicit login with CompanyDB/username/password, B1SESSION + ROUTEID cookies kept in memory (supports multi-node ServiceLayer), automatic re-login on 401, and guaranteed logout when the process exits (in addition to the sap_logout tool).

  • Self-signed TLS: support for self-signed ServiceLayer certificates (typical in local environments) via SAP_B1_VERIFY_TLS=false.

  • No telemetry or external calls: the HTTP client points exclusively to the configured URL (SAP_B1_SERVER_URL).

  • Security limits: top capped at 200 records per query.

Related MCP server: BTP MCP Server

Tools

Read (always available)

Tool

Description

sap_query

Generic GET to any OData entity with select, filter, top (≤200), skip, orderby, expand

sap_list_entities

Lists all OData entities exposed by the ServiceLayer (from $metadata, cached); includes user tables (@) and UDOs. Optional filter to narrow down

sap_get_entity_schema

Schema of an entity: properties (types/keys) and navigationProperties (valid for $expand); resolves entity sets that share an EntityType

sap_list_actions

Lists the service methods (function imports, e.g. CompanyService_GetCompanyInfo) with their parameters

sap_sql_query

Read-only SQL (SELECT/WITH; INSERT/UPDATE/DELETE/DDL rejected) via POST /sql_query — only on recent ServiceLayer v2/FP; on old v1 it responds with a clear error

sap_get_business_partners

Business partners (customers/vendors), filter by card_type

sap_get_items

Catalog items

sap_get_sales_orders

Sales orders; in v1 the lines (DocumentLines) come included without expand (expand is v2 only)

sap_get_stock

Stock of an item by ItemCode (+ optional WarehouseCode); clear error if ItemStock does not exist on the ServiceLayer (old v1)

sap_session_status

Active session status

sap_logout

Explicit session close

Write (only if SAP_B1_READONLY=false)

Tool

Description

sap_create

Creates a record in an entity (POST)

sap_update

Updates a record by its key (PATCH)

sap_delete

Deletes a record by its key (DELETE)

sap_call_action

Invokes a service method (POST); may have side effects (Cancel, UpdateCompanyInfo, Import...)

Requirements

  • Node.js 18+

  • SAP Business One 10.0 with ServiceLayer enabled (typical path https://<host>:50000/b1s/v1)

  • opencode (or any MCP client)

Configuration (environment variables)

Variable

Required

Default

Description

SAP_B1_SERVER_URL

Yes

-

ServiceLayer base URL (e.g. https://<host>:50000/b1s/v1)

SAP_B1_DATABASE

Yes

-

CompanyDB name (e.g. SBODEMO_XX)

SAP_B1_USERNAME

Yes

-

ServiceLayer user

SAP_B1_PASSWORD

Yes

-

User password

SAP_B1_READONLY

No

true

false enables the write tools

SAP_B1_VERIFY_TLS

No

true

false for self-signed certificates

SAP_B1_MAX_TOP

No

200

Maximum top limit per query

Usage with opencode

In the project's opencode.json:

{
  "mcp": {
    "sap-b1-servicelayer": {
      "type": "local",
      "command": ["npx", "-y", "github:leonardows1/sap-b1-servicelayer-mcp"],
      "environment": {
        "SAP_B1_SERVER_URL": "https://<host>:50000/b1s/v1",
        "SAP_B1_DATABASE": "<CompanyDB>",
        "SAP_B1_USERNAME": "<usuario>",
        "SAP_B1_PASSWORD": "<password>",
        "SAP_B1_SESSION_TIMEOUT": "30",
        "SAP_B1_VERIFY_TLS": "false",
        "SAP_B1_READONLY": "true"
      },
      "enabled": true
    }
  }
}

Restart opencode after saving the configuration.

Security

  • Credentials and session cookies are never logged.

  • The process only communicates with SAP_B1_SERVER_URL.

  • In READONLY=true mode the write tools are not registered: it is impossible to create/update/delete records, by design.

  • Configuration validated at startup: missing SAP_B1_SERVER_URL, SAP_B1_DATABASE, SAP_B1_USERNAME or SAP_B1_PASSWORD → the process aborts with a clear message.

  • Entity names validated (^[A-Za-z][A-Za-z0-9_]*$): routes cannot be injected (e.g. BusinessPartners/...).

  • Key values and filters escaped in OData (single quotes doubled): an id or ItemCode with ' does not break the URL or the $filter.

  • The password is stored in plain text in the MCP client configuration. Consider a secret manager if the repository is shared.

  • npx github: has no semver versioning: each run takes the latest version of the main branch. After updating the repo, use npm cache clean --force to force a reload.

Structure

Pragmatic hexagonal architecture (ESM, no framework): the domain and use cases do not know about the MCP transport or HTTP; the infrastructure implements the ServiceLayerPort port (DIP) and the MCP tools are thin controllers.

sap-b1-servicelayer-mcp/
├── package.json                  # Definición del paquete npm (bin: server.js)
├── server.js                     # Composition root: cablea dependencias y arranca stdio
├── src/
│   ├── config/
│   │   └── config.js             # Configuración desde env, validada e inmutable
│   ├── domain/
│   │   ├── errors.js             # Excepciones tipadas (Configuration/InvalidArgument/ServiceLayer)
│   │   ├── oData.js              # Helpers puros: query string, filtros, clamp de $top, validación de entidad
│   │   └── edmx.js               # Parseo puro de $metadata: entity sets, esquemas, function imports
│   ├── application/
│   │   ├── ports.js              # Puerto ServiceLayerPort (contrato, DIP)
│   │   ├── helpers.js            # ensureOk / ensureSuccess / unwrapValue
│   │   └── services/
│   │       ├── queryService.js   # Consulta GET genérica a entidades OData
│   │       ├── catalogService.js # Socios de negocio y artículos (compone QueryService)
│   │       ├── salesService.js   # Pedidos de venta y stock
│   │       ├── sessionService.js # Estado y cierre de sesión
│   │       ├── writeService.js   # create / update / delete
│   │       ├── metadataService.js # Descubrimiento: $metadata cacheado, entidades, esquemas y actions
│   │       └── sqlService.js     # SQL de solo lectura (SELECT/WITH) vía POST /sql_query
│   └── infrastructure/
│       ├── http/
│       │   ├── httpClient.js     # Cliente HTTP mínimo (http/https)
│       │   ├── cookies.js        # Manipulación pura de cookies de sesión
│       │   └── serviceLayerClient.js # Adaptador del puerto: login, 401, logout
│       └── mcp/
│           ├── result.js         # ok / err / serialize / handle (controladores delgados)
│           └── tools.js          # Registro de tools MCP
├── test/                         # node:test (sin dependencias externas)
│   ├── config.test.js
│   ├── oData.test.js
│   ├── edmx.test.js              # parseo EDMX v3/v4 (entity sets, esquemas, function imports)
│   ├── cookies.test.js
│   ├── client.test.js
│   ├── fakePort.js               # fake tipado del puerto ServiceLayerPort (compartido)
│   ├── services.test.js          # casos de uso con cliente fake (anti-inyección)
│   ├── metadataService.test.js   # descubrimiento y acciones con fake
│   ├── sqlService.test.js        # SQL solo-lectura (rechazos, Service Not Found)
│   └── tools.test.js             # integración MCP in-memory (registro y llamadas)
├── .gitignore
└── README.md

Adaptation to the real schema (verified against ServiceLayer 10.0 v1)

The server dynamically adapts to each instance's $metadata, with nothing hardcoded. Facts verified on a real instance (v1, OData v3):

  • Entity sets share an EntityType: Orders/Invoices/DeliveryNotesSAPB1.Document. sap_get_entity_schema resolves the real type automatically.

  • Document lines: in v1 they are complex collections (DocumentLines, DocumentInstallments) that come inline in the response; $expand only applies to navigationProperties (the schema lists them, e.g.: BusinessPartner, Currency).

  • Financial fields: in v1 BusinessPartners has no Balance; use CurrentAccountBalance, OpenOrdersBalance, OpenDeliveryNotesBalance. Invoices have no BalanceDue: the open balance is DocTotal − PaidToDate.

  • No ItemStock or /sql_query on old v1: sap_get_stock warns with real stock entities discovered; sap_sql_query returns a clear error.

  • V3 function imports with IsBindable="true" are listed as bound (not invocable standalone) so they do not pollute sap_list_actions.

Recipe: balance aging report (30/60/90)

Without SQL, using only sap_query (works on any v1/v2):

  1. Open invoices (paginate with skip in batches ≤200 if there are many):

    sap_query('Invoices',
      filter='PaidToDate lt DocTotal',
      select='CardCode,CardName,DocNum,DocDate,DocDueDate,DocTotal,PaidToDate,DocumentStatus,ControlAccount')
  2. For each invoice: balance = DocTotal − PaidToDate; days = today − DocDueDate.

  3. Group by ranges 0-30 / 31-60 / 61-90 / 90+ and by customer (or by ControlAccount for the view by G/L account).

  4. Totals by customer/account: sap_get_business_partners with CurrentAccountBalance (current balance) and CreditLimit.

With sap_sql_query (v2) the same report is a single query against OINV/OINV3/OFRJ/OCRD.

Development

npm install     # dependencias
npm test        # tests (node:test)
npm run typecheck  # verificación de tipos estricta (tsc --noEmit sobre JSDoc)
npm start       # arranque local (requiere variables de entorno)

All the JS code is verified with strict TypeScript via JSDoc (checkJs + strict + noUncheckedIndexedAccess): tsconfig.json with no build step, the server runs directly with node.

Manual verification (JSON-RPC over stdio)

echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | \
  SAP_B1_SERVER_URL=... SAP_B1_DATABASE=... SAP_B1_USERNAME=... SAP_B1_PASSWORD=... \
  npx -y github:leonardows1/sap-b1-servicelayer-mcp
F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

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

Related MCP Servers

  • F
    license
    A
    quality
    D
    maintenance
    Enables AI assistants to integrate with SAP systems via OData REST APIs for querying entity sets, performing CRUD operations, and executing function imports. It features automatic service discovery, CSRF token management, and smart connection handling without requiring the SAP RFC SDK.
    11
    12
  • F
    license
    A
    quality
    C
    maintenance
    Enables interaction with SAP S/4HANA systems via OData, allowing service discovery, metadata exploration, field value retrieval, and CRUD operations through natural language.
    4
    5
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables 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

View all related MCP servers

Related MCP Connectors

  • Self-hosted MCP gateway: turn any API, database or MCP server into AI connectors — no code.

  • Odoo ERP for AI agents: hosted OAuth endpoint, gated writes, one endpoint for every instance.

  • Connect your AI assistants to Keboola and expose your data, transformations, SQL queries, ...

View all MCP Connectors

Latest Blog Posts

MCP directory API

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

curl -X GET 'https://glama.ai/api/mcp/v1/servers/leonardows1/sap-b1-servicelayer-mcp'

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