Skip to main content
Glama
bersimao

b1-mcp-server for SAP B1

b1-mcp-server

A secure Model Context Protocol server that gives an AI client (e.g. Claude Code) guarded access to SAP Business One via both the database (HANA / MS SQL) and the Service Layer OData API.

Features

  • 7 MCP tools: connect_database, execute_sql, execute_service_layer (GET), execute_service_layer_write (PATCH / POST / DELETE), execute_service_layer_write_unattended (the same writes without approval, development profiles only), get_schema_info, check_connection.

  • Multi-environment: switch between client databases at runtime via named connection profiles. No restart needed.

  • Read-only by design: the SQL path executes only SELECT. Every INSERT/UPDATE/DELETE/DROP/CREATE/ALTER/EXEC is blocked server-side with no confirmation bypass — writes are handed to a human to run in a real DB client.

  • Server-side guardrails: all raw SQL submitted to execute_sql goes through the same validation whether the user or the AI wrote it. Anonymous DO BEGIN..END blocks are classified by the most dangerous statement inside them, so a write hidden behind a trailing SELECT is still a write.

  • Audit trail: selected-profile connection attempts and database/Service Layer calls that reach policy or execution are recorded as JSON Lines, including policy denials.

  • Rate limiting per tool with sliding window.

Related MCP server: SAP MCP Gateway

Install

Requires Node.js 20 or newer.

npx b1-mcp-server

Or add to your Claude Code MCP config:

{
  "mcpServers": {
    "b1-db": {
      "command": "npx",
      "args": ["-y", "b1-mcp-server"]
    }
  }
}

Dependencies

Five runtime dependencies: @modelcontextprotocol/sdk, zod, @sap/hana-client and mssql (plus generic-pool, which backs the HANA connection pool). Database access is a local driver — src/db/directDb.ts — built directly on the HANA and MS SQL clients; Service Layer traffic goes through the strict-TLS adapter in src/sl/serviceLayerAdapter.ts using Node's own fetch and https. No HTTP framework, no axios, no PostgreSQL driver.

A clean install audits at found 0 vulnerabilities with no overrides block, which also means npx -y b1-mcp-server is clean — overrides never applied there, since npx runs without a root package.json.

Connection profiles

The server starts unconnected. Connections are loaded from ~/.claude/connections.json (one profile per environment) and selected at runtime by the AI via the connect_database tool.

Profile matching behavior in connect_database:

  • Exact match by id or dbName is preferred.

  • Partial matches are accepted only when they resolve to a single profile.

  • Ambiguous partial matches are rejected with a list of candidates.

Example ~/.claude/connections.json:

[
  {
    "id": "client_a_prd",
    "dbType": "hana",
    "dbServer": "10.0.0.10:30015",
    "dbName": "SBO_CLIENT_A",
    "dbUser": "B1ADMIN",
    "dbPassword": "•••",
    "slUrl": "https://10.0.0.10:50000/b1s/v2",
    "slUser": "manager",
    "slPassword": "•••",
    "slAllowedMethods": ["GET", "PATCH", "POST"]
  },
  {
    "id": "client_b_hmg",
    "dbType": "mssql",
    "dbServer": "10.0.0.20:1433",
    "dbName": "SBO_CLIENT_B_HMG",
    "dbUser": "SA",
    "dbPassword": "•••"
  }
]

Every profile requires id, dbType and dbName. Add dbServer and dbUser to configure DirectDb; add slUrl and slUser to configure Service Layer. Each configured side also requires its non-empty password (dbPassword or slPassword). Missing/empty passwords fail locally without a login attempt, avoiding an unnecessary failure that may count toward account lockout under the configured login policy. Non-empty password strings are passed through without trimming. Either side, or both sides, can be configured in one profile.

slAllowedMethods (optional) lists the HTTP verbs the Service Layer tools may send for that profile: any of GET, PATCH, POST, DELETE (case-insensitive). Without it a profile is read-only (["GET"]); every write verb must be listed explicitly. PUT is not supported, because it replaces the whole entity and wipes every field the body omits. An invalid value (unknown verb, empty list, not an array) skips that profile instead of guessing. The list is applied on connect and printed by connect_database; editing it re-logs the Service Layer on the next connect_database call. Allowing a write verb does not waive approval: every write still needs the per-call human acceptance described under the security posture.

slWriteApproval (optional) chooses who approves a write. "elicitation" (default): the server asks through an MCP approval form. "client": the server sends no form and relies on the MCP client's own permission prompt — for setups where the form cannot reach the user, such as Claude Code Remote Control, which forwards permission prompts but not MCP forms. client is honoured only when the MCP client identifies itself as Claude Code; any other client falls back to elicitation. The server cannot see that prompt, so the mode is only as strong as the client's permission settings: list mcp__<server-name>__execute_service_layer_write under permissions.ask, never allow, and do not use it with --dangerously-skip-permissions. Without that rule, answering the first prompt with "Yes, don't ask again" saves an allow rule and every later write runs unprompted. Keep production profiles on elicitation. connect_database prints the mode that applies to the current client; editing it re-logs the Service Layer.

"none" is for development companies where a prompt on every write slows the work down. execute_service_layer_write behaves as in client mode, and the profile also unlocks execute_service_layer_write_unattended, which runs the same writes with no approval at all, for any MCP client. That tool refuses every profile not set to "none", so you can list mcp__<server-name>__execute_service_layer_write_unattended under permissions.allow and keep the normal write tool under permissions.ask: development profiles run unprompted and every other profile still prompts. The opt-in lives only in the connections file, so it is only as safe as that file: never set "none" on a profile that points at production data.

Both SAP Service Layer roots are supported: /b1s/v1 for OData v3 and /b1s/v2 for OData v4. Login, health checks and relative Service Layer requests use the exact root configured by the profile. Request validation is version-neutral; callers pass only the relative endpoint (for example, Items?$select=ItemCode&$top=1). OData query and payload differences remain the caller's responsibility; configure the root supported by the target SAP B1 installation. SAP deprecated OData v3 starting with SAP Business One 10.0 FP 2405 and made OData v4 the primary protocol, so prefer /b1s/v2 when the target installation supports it; see the Service Layer API reference.

Security model

The server does not intentionally interpolate profile passwords into MCP tool responses or audit entries. Passwords are read from ~/.claude/connections.json inside the server process and passed directly to the configured database or Service Layer endpoint for login. The Service Layer adapter retains only the session cookie afterward, not the username or password. Database-driver and Service Layer error messages are returned and audited verbatim for diagnosis, however, so there is no general-purpose secret redaction boundary around arbitrary upstream error text. Do not place secrets in profile IDs, database names, URLs or other non-password metadata. This protocol-level protection also does not stop an AI host that has unrestricted same-user shell or filesystem access from opening the file directly; see the hook guidance below.

Service Layer connections require HTTPS with normal certificate validation by default. For an internal certificate authority, set NODE_EXTRA_CA_CERTS to the CA bundle before starting the MCP process.

For a legacy SAP installation whose certificate cannot be renewed, the first connect_database call inspects TLS without sending credentials and requests explicit approval through MCP form elicitation. An accepted certificate is stored by canonical https://host:port origin in ~/.claude/service-layer-trust.json (mode 0600), so profiles sharing one SL endpoint share one pin. The file is created after explicit approval or after a matching legacy manual profile pin is migrated successfully, and contains no credentials. A changed certificate requires approval again; clients without elicitation support fail closed. Legacy manual profile pin fields remain accepted only for that one-time migration into this local store.

Once a certificate is pinned, that exact fingerprint is the whole check — hostname verification is not applied on top of it. SAP B1 issues its self-signed Service Layer certificate to the machine's short name (DNS:MYSERVER) while clients normally connect by FQDN, so re-verifying the name would reject an already-approved certificate with no way to accept it. slTlsServerName is therefore only the SNI name sent during the handshake, for hosts that serve a different certificate per name; it is not needed to make a pinned FQDN connection work. Service Layer URLs may use DNS names, IPv4 or IPv6. For an IP URL, SNI is omitted unless slTlsServerName supplies a DNS hostname; a legacy IP-valued slTlsServerName is accepted as a no-op. Older trust-store records may still carry serverName as a compatibility fallback, but new SNI configuration belongs in the connection profile rather than in certificate trust metadata.

Profiles reload on every connect_database call. A DB-only profile can gain slUrl, slUser and slPassword while its DB connection is active; the next call keeps that DB connection and initializes only Service Layer.

Global certificate verification disablement is never supported: the adapter refuses to connect when NODE_TLS_REJECT_UNAUTHORIZED=0 is present.

What the AI does see when it calls connect_database with "list":

  • profile id

  • dbName, dbType

  • a capability flag (DB, SL, DB+SL)

  • an active marker when either configured side is currently connected

Recommendations for your connections.json:

  • Lock the file to your user only.

    • Linux/macOS: chmod 600 ~/.claude/connections.json.

    • Windows: icacls "%USERPROFILE%\.claude\connections.json" /inheritance:r /grant:r "%USERNAME%:F".

  • Do not commit it to any git repository or share it via cloud sync.

  • On POSIX systems, the server fails closed and refuses to load a profile file that grants any permission to group or other users.

  • Each user maintains their own copy. Credentials never come from this npm package.

Claude Code hook and sandbox guidance

Use a centrally managed Claude Code PreToolUse hook or equivalent host policy to deny AI-issued access to the canonical resolved path of connections.json. Cover every filesystem-capable tool (Read, Grep, Glob, edit/write tools and filesystem MCP servers), not only literal filename matches. Shell commands are the difficult case: matching the path text is not sufficient because commands, working directories and symlinks can access it indirectly. Prefer a host filesystem sandbox that denies the credential path; otherwise require approval for shell access outside the workspace.

The hook/policy must live outside AI-writable project and user settings. For Claude Code enterprise deployments, install it in the platform's managed settings so project instructions cannot override it. Deny AI writes to service-layer-trust.json as well; reading that file is not secret, but its integrity controls which legacy certificate is trusted.

Hooks are defense in depth, not an OS security boundary. They govern tool calls issued through the AI host; they cannot stop this MCP process, another local process, or a malicious npm package running as the same OS user from reading a mode-0600 file. Strong isolation requires a dedicated OS identity, sandbox, or remotely hosted credential broker. See the Claude Code hooks and server-managed settings guidance.

Configuration (env vars)

All optional.

There is no .env file. The server reads process.env directly and never loads a dotenv file, so dropping a .env next to it does nothing. For a stdio MCP server the client owns the environment — set variables in the env block of your MCP configuration, which the client passes to the spawned process:

{
  "mcpServers": {
    "b1-db": {
      "command": "npx",
      "args": ["-y", "b1-mcp-server"],
      "env": {
        "MCP_QUERY_TIMEOUT_MS": "120000",
        "MCP_SL_WRITES_ENABLED": "false"
      }
    }
  }
}

Credentials are not configured this way — they live only in connections.json, which is resolved from your home directory and therefore found regardless of where npx runs from.

Variable

Default

Purpose

MCP_CONNECTIONS_FILE

~/.claude/connections.json

Path to profile file

MCP_AUDIT_LOG_PATH

~/.claude/logs/b1-mcp-audit.jsonl

Audit log JSONL file. Unset or empty uses the default path

MCP_LOG_LEVEL

info

Stderr verbosity: debug / info / warn / error. JSONL file logging is unaffected

MCP_MAX_QUERY_LENGTH

8000

Max SQL length in characters

MCP_RATE_LIMIT_MAX_CALLS

60

Max calls per tool per window

MCP_RATE_LIMIT_WINDOW_MS

60000

Rate-limit window

MCP_QUERY_TIMEOUT_MS

60000

Statement ceiling. A query still running at this point is aborted. Raise it if a legitimate report needs longer

MCP_SL_TIMEOUT_MS

30000

Service Layer login/request timeout

MCP_SL_TRUST_FILE

~/.claude/service-layer-trust.json

Local non-secret certificate trust store

MCP_SL_MAX_URL_LENGTH

2048

Maximum relative OData URL length

MCP_SL_MAX_BODY_CHARS

50000

Maximum serialised PATCH/POST body length

MCP_SL_WRITES_ENABLED

true

Emergency kill switch for every Service Layer write (PATCH, POST, DELETE); true/false are case-insensitive and invalid values disable writes. Replaces MCP_SL_PATCH_ENABLED, which is no longer read — except that any value other than true in the old name still disables writes, so an upgrade never re-opens them

MCP_ELICITATION_TIMEOUT_MS

120000

How long a human gets to answer an approval form (certificate trust, Service Layer write) before it fails closed

MCP_MAX_RESULT_ROWS

500

Max rows returned to the model; extra rows are cut and announced

MCP_MAX_RESULT_CHARS

100000

Max characters of result JSON, applied after the row cap

MCP_DRY_RUN

false

The exact value true validates raw SQL and Service Layer writes without executing them; connections and Service Layer GET still run

The numeric limits above must be positive integers. An unset or whitespace-only value uses the default. Any other invalid value (including 0, a negative or a non-integer) reports the fallback on stderr and uses the default — a misconfigured limit never means "no limit". MCP_LOG_LEVEL likewise uses info when unset/empty and reports a fallback for any other invalid value.

Guardrail summary

The SQL path is read-only, so the live answer does not depend on which table you touch:

Operation

Any table (SAP core, UDT, custom, temp)

SELECT

allow, subject to the SELECT-specific restrictions below

INSERT / UPDATE / DELETE / DROP

block — run it yourself in a DB client

CREATE / ALTER

block

EXEC / EXECUTE / CALL

block — stored procedures cannot be run through this server

Also blocked: multi-statement queries (semicolons outside BEGIN..END), unterminated quotes or brackets, sequence advancement (NEXTVAL / NEXT VALUE FOR), OPENQUERY / OPENROWSET / OPENDATASOURCE, SELECT ... INTO <table>, and high-impact SQL Server lock hints (UPDLOCK, XLOCK, TABLOCK, TABLOCKX, HOLDLOCK, SERIALIZABLE, REPEATABLEREAD).

Service Layer requests allow only the verbs in the profile's slAllowedMethods (default GET only); PUT is never available. PATCH and DELETE require one directly keyed entity endpoint; POST accepts an entity set, a service operation or one action on a keyed entity (Orders(12)/Close); writes take no query options. Writes go through a separate tool, execute_service_layer_write, so a client can gate them by tool name while GETs stay unprompted. Every write names its target database, which must match the connected Service Layer. By default every write requires explicit user acceptance through MCP form elicitation. The approval screen is bound to the database, Service Layer root, endpoint, exact body, field list and SHA-256 body hash. A client without elicitation support cannot write. A profile with slWriteApproval: "client" replaces the form with Claude Code's permission prompt, and one with slWriteApproval: "none" also accepts unapproved writes through execute_service_layer_write_unattended (see the profile fields above). MCP_DRY_RUN=true never executes a write, and MCP_SL_WRITES_ENABLED=false disables every write globally.

The per-table classification model (SAP_CORE / SAP_USER / CUSTOM / TEMP with per-operation rules) still exists in src/guardrails/rules/ and is fully tested, but on the read-only server it is not reachable from the live path. See SECURITY.md for the full model and its known limitations.

Agent skill

skills/b1-mcp-server/SKILL.md teaches an AI agent how to use these tools: connect first, what each refusal means and what to do about it, why a HANA timeout reports a dropped socket, how truncated results look, and how Service Layer writes are approved. To install it in Claude Code, copy the folder into ~/.claude/skills/; other agents that read SKILL.md files take it from their own skills directory.

Development

npm install
npm run build
npm run test
npm run test:watch
# Optional aliases (same behavior, useful if you prefer explicit naming):
npm run test:bash
npm run test:watch:bash

From a source checkout only, run this quick connection sanity check using a profile from connections.json (scripts/ is not shipped in the npm tarball):

npx tsx scripts/test-connection.ts <profile-id>

License

MIT — see LICENSE.

Available Tools

7 tools
check_connectionA

Check if the database and Service Layer connections are active and alive. Pings DirectDb (SELECT query) and Service Layer (GET request) independently. No parameters required. Reports status for both connections.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior4/5

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

With no annotations, the description carries the full burden and does well: it discloses that it issues a SELECT against DirectDb and a GET against Service Layer, making clear this is a non-mutating probe, and that the two checks run independently. It does not state failure semantics (e.g., whether one failed connection causes an error or a partial report).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four short sentences, front-loaded with the purpose, followed by mechanism, then call mechanics and output. Every sentence adds information and none is redundant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description must hint at returns; 'Reports status for both connections' covers the shape adequately for a simple health check. It is complete enough to invoke correctly, with only the exact status format and failure behavior left unspecified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so the baseline is 4. The description explicitly confirms 'No parameters required', which matches the empty input schema and removes any ambiguity for the agent.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb (check/ping) and two specific resources (DirectDb, Service Layer), and states the exact mechanism used for each. It implicitly distinguishes itself from siblings like connect_database and execute_sql by being a status probe rather than an action, though it never names an alternative explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains what happens but not when to prefer this over connect_database or execute_sql, nor any precondition or exclusion. The only usage-adjacent statement is 'No parameters required', which is a call mechanic, not guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

connect_databaseA

Switch the active database and Service Layer connection to a different SAP Business One environment. Searches connection profiles by ID or database name (case-insensitive). Partial matches are only accepted when they resolve to a single profile.

Each profile can include DirectDb (DB) credentials, Service Layer (SL) credentials, or both. When switching environments, each stale or non-matching side is disconnected before replacement; a healthy side that still matches the selected profile remains active. If only one side connects successfully, it remains active - retry the failed side after fixing credentials. Tracks failed login attempts per profile and warns about account lockout risk under the configured login policy.

Available profiles: (no profiles loaded)

Use "list" as the query to reload and list all available profiles.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesThe database identifier to connect to. Can be the profile ID or database name. Use "list" to see all available profiles.

TDQS

A4.1/5.0
Behavior4/5

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

With no annotations, the description carries the full burden and does so well: it discloses that stale/non-matching sides are disconnected before replacement, that a healthy matching side stays active, that a partial single-side connection persists until retried, and that failed logins are tracked with lockout warnings. It omits auth prerequisites and what the tool returns, but the mutation semantics are unusually well spelled out.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose, then progressively narrower detail on matching, profile composition, and switch behavior. Reasonably tight for the number of behaviors disclosed; the 'Available profiles:' block is dynamic state rather than prose, and no sentence is pure filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter connection-switching tool with no annotations and no output schema, the description covers the important behaviors: search resolution, dual-side profiles, disconnection order, partial-connect outcomes, and lockout risk. Only the response shape after a successful switch is left unstated, which is a minor gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the schema already documents ID-or-name lookup and the 'list' keyword, so the baseline is 3. The description adds real meaning beyond that: matching is case-insensitive and partial matches are accepted only when they resolve to a single profile, which materially changes how an agent should format the query.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Switch the active database and Service Layer connection to a different SAP Business One environment.' None of the siblings (execute_service_layer, execute_sql, check_connection, get_schema_info, execute_service_layer_write*) perform connection switching, so the agent can separate it by scope alone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives operational guidance for the 'list' query and for retrying a failed side, which is useful invocation context. However, it never states when to reach for this tool versus check_connection or the execute_* siblings, nor any explicit exclusion, so usage remains implied rather than directed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

execute_service_layerA

Execute a read-only OData GET against the currently connected SAP Business One Service Layer. Writes go through execute_service_layer_write.

Denied when the connected profile's slAllowedMethods (printed by connect_database) omits GET. Query options are allowed; absolute URLs, Login/Logout and $batch are denied.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesRelative OData endpoint without the configured /b1s/v1/ or /b1s/v2/ root
methodNoAlways GET. PATCH, POST and DELETE go through execute_service_layer_writeGET

TDQS

A4.6/5.0
Behavior4/5

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

With no annotations, the description carries the full burden and does well: it discloses the read-only nature, the profile-driven authorization gate, and where to inspect that gate (connect_database prints slAllowedMethods). It stops short of describing error responses or pagination behavior, which for a remote HTTP-style call the agent may still need.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences with no filler; the core operation comes first, followed by the routing and denial rules. Every clause earns its place by adding a routing or constraint fact.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a permission-gated remote call with no output schema, the description supplies the operation, the alternative, the authorization precondition, and the blocked endpoints, which is nearly everything needed. It omits only what a successful/error response looks like, which an agent might want before composing follow-up calls.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds genuine constraint information about the url parameter: query options are accepted while absolute URLs, Login/Logout, and $batch are rejected. That meaning is not derivable from the schema description alone, which only says the endpoint is relative.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Execute a read-only OData GET against ... SAP Business One Service Layer') and immediately names the sibling that handles the complementary operation, execute_service_layer_write. An agent can distinguish this from every sibling without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes writes elsewhere ('Writes go through execute_service_layer_write'), states the exact condition under which the call is denied (slAllowedMethods omitting GET), and enumerates what is permitted (query options) versus denied (absolute URLs, Login/Logout, $batch). Both when-to-use and when-not are covered.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

execute_service_layer_writeA

Execute a guarded OData write (PATCH, POST or DELETE) against the currently connected SAP Business One Service Layer. These are real writes to the company database.

The verb must be in the connected profile's slAllowedMethods, and database must equal the connected Service Layer database exactly — connect_database prints both. A mismatch is denied.

Approval follows the profile's slWriteApproval, also printed by connect_database. "elicitation" (default): the server asks the user through an MCP approval form; clients without form elicitation cannot write. "client" or "none": the server executes after this client's own permission prompt, honoured only for Claude Code. On a "none" profile, prefer execute_service_layer_write_unattended, which does the same write without any approval; use this tool there only when the user asks to confirm the write. MCP_DRY_RUN previews but never executes a write, and MCP_SL_WRITES_ENABLED=false disables every write globally.

PATCH and DELETE accept one directly keyed entity endpoint only. POST accepts an entity set, a service operation, or one action on a keyed entity (Orders(12)/Close). Query options, navigation paths, absolute URLs, Login/Logout and $batch are denied.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesRelative OData endpoint without the configured /b1s/v1/ or /b1s/v2/ root
bodyNoJSON object: required and non-empty for PATCH, optional for POST, forbidden for DELETE
methodYesMust also be allowed by the connected profile's slAllowedMethods
databaseYesConnected company database exactly as connect_database prints it; shown to the approver and checked by the server

TDQS

A4.8/5.0
Behavior5/5

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

No annotations exist, so the description carries the full burden and does so: it warns these are real database writes, describes the exact denial condition for method/database mismatch, explains the three approval paths and which clients honour them, and enumerates denied endpoint shapes (query options, navigation, $batch, Login/Logout). This is far beyond what the schema conveys.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three dense paragraphs, front-loaded with the verb and the write warning, then gating rules, then endpoint restrictions. Almost every sentence carries a constraint, though the approval-mode enumeration is long enough that a bulleted form would scan faster.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a high-risk, four-parameter mutation tool with no annotations and no output schema, the description covers permissions, database matching, approval routing, dry-run and global kill switches, and accepted endpoint shapes. The only omission is error/response semantics, which is minor for a write operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real constraints the schema does not: method must be in the profile's slAllowedMethods, database must equal the connected Service Layer database exactly, and PATCH/DELETE accept only one keyed entity endpoint while POST accepts a set, operation, or single action. Body optionality is already covered by the schema, so it is not a full 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (execute) and resource (guarded OData write: PATCH/POST/DELETE against the SAP B1 Service Layer) and immediately scopes it as real company-database writes. It is clearly distinguishable from the read-oriented execute_service_layer sibling and from execute_service_layer_write_unattended, which it names directly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when writes are permitted (verb in slAllowedMethods, database must match connect_database exactly), how approval behaves per slWriteApproval mode, and when to prefer execute_service_layer_write_unattended over this tool. It also names MCP_DRY_RUN and MCP_SL_WRITES_ENABLED=false as global gates, leaving nothing to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

execute_service_layer_write_unattendedA

Execute a guarded OData write (PATCH, POST or DELETE) with NO approval of any kind, for development companies. Denied unless the connected profile has slWriteApproval "none" (printed by connect_database); otherwise use execute_service_layer_write. These are real writes to the company database.

Every other rule of execute_service_layer_write applies unchanged: database must equal the connected Service Layer database, the verb must be in slAllowedMethods, the same endpoint shapes are enforced, MCP_DRY_RUN previews and MCP_SL_WRITES_ENABLED=false disables it.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesRelative OData endpoint without the configured /b1s/v1/ or /b1s/v2/ root
bodyNoJSON object: required and non-empty for PATCH, optional for POST, forbidden for DELETE
methodYesMust also be allowed by the connected profile's slAllowedMethods
databaseYesConnected company database exactly as connect_database prints it; shown to the approver and checked by the server

TDQS

A4.8/5.0
Behavior5/5

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

No annotations are provided, so the description carries the full burden, and it does: it warns these are real writes to the company database, states the denial condition, discloses that dry-run previews apply, and that an env flag can disable the tool entirely. It also reiterates the inherited guard rules (database must match, verb must be in slAllowedMethods).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the tool's distinguishing property (no approval) before the denial condition and inherited rules. Both paragraphs earn their place, though the second paragraph is dense and could be tightened slightly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with no annotations and no output schema, the description covers the safety-critical facts an agent needs: approval gating, denial behavior, dry-run and feature-flag effects, and the inherited endpoint/verb constraints. Nothing essential to correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real semantics beyond the schema: database must equal the connected Service Layer database and the verb must also be permitted by the profile's slAllowedMethods. It does not add syntax or body-shape detail, but the constraint context is valuable.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (execute a guarded OData write) with the exact allowed methods (PATCH, POST, DELETE) and scope (no approval), and explicitly names the sibling it is not: use execute_service_layer_write when approval doesn't apply. An agent can distinguish it from the approval-gated sibling without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit gating condition — denied unless the connected profile's slWriteApproval is "none" (a value printed by connect_database) — and names the alternative tool to use otherwise. It also clarifies the environment conditions (MCP_DRY_RUN previews, MCP_SL_WRITES_ENABLED=false disables it).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

execute_sqlA

Execute a raw SQL statement against the currently connected database. All SQL, whether written by the user or generated by you, goes through the same server-side validation.

IMPORTANT: Before calling this tool, you MUST confirm which database the user intends to query. If the user has not explicitly stated the target database in their message, ASK them first. Do NOT assume the currently connected database is the intended target.

READ-ONLY: only SELECT (and read-only DO BEGIN...END blocks) execute. Data-changing statements are blocked server-side.

If the user needs a write (INSERT/UPDATE/DELETE), do NOT call this tool — generate the SQL and give it to the user to run in a real DB client (HANA Studio / DBeaver / hdbsql), where a human can review the statement and control its transaction handling.

Rules enforced server-side:

  • READ-ONLY: INSERT/UPDATE/DELETE/DROP are blocked for everyone — no confirmation bypass.

  • Direct EXEC/EXECUTE/CALL: blocked (stored procedures are disabled).

  • CREATE/ALTER: blocked.

  • MS SQL pass-through (OPENQUERY/OPENROWSET/OPENDATASOURCE): blocked.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesThe SQL statement to execute (SELECT only — read-only server)

TDQS

A4.3/5.0
Behavior5/5

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

With no annotations, the description carries the full burden and does so: server-side read-only enforcement, an enumerated block list (INSERT/UPDATE/DELETE/DROP, EXEC/CALL, CREATE/ALTER, OPENQUERY pass-through), and that there is no confirmation bypass. It also notes the reason writes are excluded (human review and transaction control), which is behaviorally meaningful.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose, then clearly sectioned with IMPORTANT / READ-ONLY / Rules headings that make it scannable. The read-only restriction is stated two or three times (prose, rule list, schema), which is mild redundancy, but the length is justified by the safety-critical content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations and no output schema, the description is impressively complete on safety and routing. It is silent on operational limits that matter for a SQL executor (row caps, timeouts, pagination, or what the result set looks like), which is the only notable gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

One parameter with 100% schema coverage, so the schema already documents 'query'. The description only restates that it is SELECT-only, which duplicates the schema's own description; baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a precise verb and resource ('Execute a raw SQL statement against the currently connected database'), which is unambiguous. It does not, however, name or contrast itself against the sibling tools execute_service_layer / execute_service_layer_write, so an agent must infer the raw-SQL-vs-service-layer split.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states the precondition (the user MUST have named the target database, otherwise ask first), the allowed case (SELECT only), and the exclusion with a concrete alternative (writes should be handed to the user to run in HANA Studio / DBeaver / hdbsql). Both when-to-use and when-not-to-use are covered with a redirect.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_schema_infoA

Retrieve metadata about tables, columns, or procedures in the currently connected database. Read-only. Use this to explore the schema before writing queries. For "columns": provide the table name in "filter". For "tables"/"procedures": filter is optional (LIKE pattern).

IMPORTANT: Before calling this tool, you MUST confirm which database the user intends to query. If the user has not explicitly stated the target database in their message, ASK them first. Do NOT assume the currently connected database is the intended target.

ParametersJSON Schema
NameRequiredDescriptionDefault
filterNoFor columns: table name (required). For tables/procedures: optional LIKE pattern.
objectTypeYesType of metadata to retrieve

TDQS

A4.1/5.0
Behavior4/5

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

No annotations are provided, so the description carries the full burden — and it does disclose the key trait ('Read-only') plus the scope constraint ('currently connected database') and a safety precondition about not assuming the target DB. It omits any statement about permissions, result volume, or pagination limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Per-objectType filter guidance is front-loaded and every sentence relates to correct invocation. The IMPORTANT block is slightly repetitive ('ASK them first' followed by 'Do NOT assume'), which costs a little tightness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter read tool with no output schema, the description covers purpose, safety, parameter behavior, and the critical database-confirmation precondition. The only gap is that it says nothing about return shape, though metadata output is largely predictable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the description's filter guidance (table name required for columns, optional LIKE pattern otherwise) essentially restates what the schema already declares for both properties. It adds salience for the conditional requirement but no new semantics, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource — retrieving metadata about tables, columns, or procedures — and scopes it to the currently connected database. This clearly separates it from the execute_sql / execute_service_layer siblings, which run statements rather than describe schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit usage context ('explore the schema before writing queries') and a hard precondition (confirm the user's target database, ask if unstated). It also spells out per-objectType filter behavior. It stops short of naming an alternative tool for the query-writing task, so it does not reach the full when/when-not/alternatives bar.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 7 tool updatesv1.5.1
    • First observedcheck_connection
    • First observedconnect_database
    • First observedexecute_service_layer
    • First observedexecute_service_layer_write
    • First observedexecute_service_layer_write_unattended
    • First observedexecute_sql
    • First observedget_schema_info

TDQS

A4.2/5.0

Scored across 7 tools

Disambiguation4/5

Read vs write tools are clearly separated (execute_service_layer vs execute_service_layer_write), and execute_sql/check_connection/get_schema_info target distinct concerns. The only real ambiguity is between execute_service_layer_write and execute_service_layer_write_unattended, which perform the identical operation and differ only in approval mode; the descriptions explain the distinction but an agent can still easily misselect.

Naming Consistency5/5

All tools use snake_case with a consistent verb_noun pattern (execute_*, connect_*, check_*, get_*). The write/write_unattended pair follows a predictable suffix convention rather than breaking the pattern. Names are readable and self-describing throughout.

Tool Count5/5

Seven tools is well-scoped for a DB/SAP integration server: connection management, status, schema introspection, read/write SQL, and read/write Service Layer. Each tool earns its place with no obvious filler or redundancy.

Completeness4/5

The surface covers connection lifecycle, schema exploration, status checks, and both read and write paths across SQL and Service Layer. The main intentional gap is direct SQL writes (blocked by design, handed to the user), and there is no explicit disconnect tool, but core workflows are covered.

Maintenance

ActivityNo data
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • 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
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    A reference implementation for connecting SAP Business One to MCP-compatible AI clients, enabling read-only SQL analysis and guarded write workflows.
    1
    -
  • F
    license
    Not graded
    quality
    B
    maintenance
    Connects AI assistants to SAP Business One ServiceLayer, enabling discovery and query of OData entities, service methods, and safe read-only operations by default, with optional write access and simple npx execution.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to discover SAP B1 entities, inspect their schemas, and perform authenticated CRUD operations through natural language, translating requests into Service Layer API calls.
    208 npm
    MIT