Skip to main content
Glama

dsp-mcp

A local MCP (Model Context Protocol) server that exposes the 19 SAP Datasphere CLI skills from dsp-cli as MCP tools over Streamable HTTP, so hosted assistants (like SAP Joule) can drive Datasphere modeling by URL.

The server is a thin wrapper: each MCP tool call spawns the corresponding node --env-file=<dsp-cli>/.env skills/<name>/<name>.js … child process and streams its stdout back to the caller verbatim. All authentication, OAuth token caching, and Datasphere logic stays in dsp-cli.

Disclaimer. This is a personal, unofficial project. It is not an SAP product and is in no way endorsed, supported, or maintained by SAP. There is no support and no warranty: use it entirely at your own risk, and you are responsible for anything you do with it. See LICENSE.

Attribution. The 19 bundled Datasphere CLI skills are third-party code by Frederic Wall (dsp-cli, a fork of yuyonggang/dsp-cli), redistributed under their ISC license. This repository adds only the MCP wrapper around them. See NOTICE for full attribution, licenses, and trademarks.

Quick start

# 1. Configure
cp .env.example .env
# edit .env: set DSP_CLI_DIR plus your Datasphere credentials
# (DATASPHERE_HOST, CLIENT_ID, CLIENT_SECRET, SPACE) — all required

# 2. Install
npm install

# 3. Run
npm run dev

You'll see a banner like:

[dsp-mcp] listening on http://127.0.0.1:3333/mcp
[dsp-mcp] health:      http://127.0.0.1:3333/health
[dsp-mcp] tools:       19 registered
[dsp-mcp] dsp-cli dir: /Users/…/dsp-cli  [ok]
[dsp-mcp] env file:    /Users/…/dsp-cli/.env  [ok]
[dsp-mcp] configure Joule with URL: http://127.0.0.1:3333/mcp

Point Joule (or any MCP client that speaks Streamable HTTP) at http://127.0.0.1:3333/mcp.

Related MCP server: CI MCP Server

Desktop app (menu bar)

Prefer not to babysit a terminal? A small macOS menu bar app (Electron) can run the server for you — Start/Stop, Copy URL, Logs, and Preferences from a status-bar icon, installable as a .dmg. It spawns this same server as a child process (system Node required; the server code is unchanged). The 19 skills are bundled inside the app, so users only enter their Datasphere credentials in Preferences — no separate dsp-cli checkout needed (though an own DSP_CLI_DIR can override the bundled skills).

cd desktop
npm install
npm run dev                                  # develop against the live tray app
DSP_CLI_SRC=/path/to/dsp-cli npm run dist    # build desktop/dist/dsp-mcp-<version>.dmg

See desktop/README.md for details. From the repo root you can also use npm run desktop:dev / npm run desktop:dist.

Cloud Foundry (SAP BTP)

The server also runs on Cloud Foundry so a hosted assistant can reach the MCP endpoint over a public URL. The dsp-cli skills are bundled into this repo under cf/dsp-cli-src/ (source, committed) and vendored to cf/vendor/dsp-cli/ (generated by heroku-postbuild, git-ignored) — no separate dsp-cli checkout is needed in the container.

# One-time: seed the OAuth token (from a prior local browser login) so the CLI
# authenticates headless. secrets.json holds access + refresh tokens.
B64=$(base64 -i ~/.@sap/datasphere-cli/.cache/secrets.json | tr -d '\n')

cf push
cf set-env dsp-mcp DATASPHERE_HOST "https://<tenant>.hcs.cloud.sap"
cf set-env dsp-mcp CLIENT_ID '<client-id>'         # single-quote: XSUAA ids contain ! and |
cf set-env dsp-mcp CLIENT_SECRET '<client-secret>'
cf set-env dsp-mcp SPACE '<space>'
cf set-env dsp-mcp DSP_SECRETS_B64 "$B64"
cf restart dsp-mcp

manifest.yml sets MCP_HOST=0.0.0.0, MCP_ALLOWED_HOSTS (the route host, for the SDK's DNS-rebinding guard), and DSP_CLI_DIR=cf/vendor/dsp-cli. Key notes:

  • Headless auth: the CLI's normal login is an interactive browser flow. On CF, DSP_SECRETS_B64 is written into the CLI token cache at startup and DSP_SKIP_LOGIN tells the skills to use it instead of logging in. The refresh token is valid ~180 days — re-seed DSP_SECRETS_B64 when it expires.

  • Endpoint auth: the MCP endpoint itself is currently open (sandbox). Add an auth layer before any non-sandbox use.

  • Secrets: DSP_SECRETS_B64 / CLIENT_SECRET are plaintext in the app env here; for production use a credential/user-provided service instead.

Smoke tests

# Health
curl -sS http://127.0.0.1:3333/health

# tools/list (JSON-RPC over Streamable HTTP)
curl -sS -X POST http://127.0.0.1:3333/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

# initialize
curl -sS -X POST http://127.0.0.1:3333/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'

# call a read-only tool
curl -sS -X POST http://127.0.0.1:3333/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list-objects","arguments":{}}}'

Offline registry dump:

npm run list-tools           # human-readable
npm run list-tools -- --json # machine-readable

Configuration

All settings come from this project's own .env (see .env.example):

Var

Default

Purpose

DSP_CLI_DIR

(required)

Directory containing skills/. Locally, your dsp-cli checkout; on Cloud Foundry, the bundled copy cf/vendor/dsp-cli (see below).

DATASPHERE_HOST

(required)

Datasphere tenant URL, forwarded to each skill.

CLIENT_ID / CLIENT_SECRET

(required)

OAuth client credentials, forwarded to each skill.

SPACE

(required)

Default space ID used when --space is not passed.

DSP_ENV_FILE

(unset)

Local convenience only: if set, this file is loaded into the environment at startup (lets an existing dsp-cli/.env keep working). Leave unset on Cloud Foundry.

MCP_HOST

127.0.0.1

Bind address (localhost only for DNS-rebinding safety)

MCP_PORT

3333

Port

SKILL_TIMEOUT_MS

300000 (5 min)

Per-skill hard timeout

MCP_INCLUDE_STDERR

0

When 1, append child stderr to tool responses even on success

The server reads DATASPHERE_HOST, CLIENT_ID, CLIENT_SECRET, and SPACE from its own environment and passes them to each skill's child process (the skills read them straight from process.env). This single source of truth works the same on a laptop (dotenv-loaded .env) and on Cloud Foundry (platform env vars / a user-provided service) — no on-disk credential file is required at runtime.

Adding a skill

The registry is the only file you need to edit. Add one entry to SKILLS in src/skills.ts:

{
  name: 'my-new-skill',                                     // MCP tool name
  description: 'One-line summary shown to the LLM.',
  entry: entry('my-new-skill', 'my-new-skill.js'),          // resolved from skillsDir
  category: 'inspect',
  mutates: false,
  inputSchema: {
    thing: z.string().describe('What to look at'),
    ...spaceField,                                          // shared --space field
  },
  flags: {
    thing: { flag: '--thing', kind: 'value' },
    space: spaceFlag,                                       // shared --space mapping
  },
},

No other file changes needed — src/index.ts iterates SKILLS and registers each.

Tool catalog

Nineteen tools, grouped by category (from npm run list-tools):

  • Create — create-local-table, create-view, create-analytic-model, create-model, create-data-flow, create-replication-flow, create-transformation-flow

  • Inspect — list-objects, read-object, describe-model, impact-analysis

  • Modify — add-columns-to-table, add-columns-to-view, rename-column, remove-column

  • Cascade — propagate-columns, rename-column-cascade, remove-column-cascade

  • Lifecycle — export-model

Troubleshooting

  • skill env … [MISSING] on startup — one or more of DATASPHERE_HOST, CLIENT_ID, CLIENT_SECRET, SPACE are not set. Add them to .env (see .env.example), or on Cloud Foundry set them as platform env vars. The startup warning names exactly which are missing.

  • skill 'X' failed (exit 1): Missing required environment variables: DATASPHERE_HOST — same problem: the server had no credentials to forward. Check the four vars above.

  • Tool call hangs then returns "timed out after …" — bump SKILL_TIMEOUT_MS. Some cascade operations on large graphs need more than 5 minutes.

  • Host: evil.example returns 4xx — that's the SDK's DNS-rebinding protection. Legitimate clients speaking Host: 127.0.0.1 (or localhost) are accepted.

Security

  • Server binds to 127.0.0.1 only. Nothing outside your machine can reach it.

  • SDK-provided Host-header validation blocks DNS-rebinding attacks against localhost.

  • No auth on the MCP endpoint itself: anyone with a shell on this machine can call mutating tools. Acceptable for a single-user dev laptop; do not run this on a shared host.

⚠️ Do not expose this publicly without adding authentication. The Cloud Foundry path above binds 0.0.0.0 behind a public router with no auth on the MCP endpoint. Anyone who finds the URL can call mutating tools against your Datasphere tenant. The deployment steps are a sandbox example only. Before any real use, put an auth layer in front of the endpoint and store credentials in a user-provided service, not plaintext env vars.

Concurrency

Each tool call spawns a Node child. Concurrent modeling operations targeting the same Datasphere object are a Datasphere-side race — the MCP server does not serialize them. Rely on your caller (e.g. Joule) to sequence dependent operations.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Production-ready MCP server that enables AI assistants to seamlessly interact with SAP Datasphere environments for real tenant data discovery, metadata exploration, analytics operations, ETL data extraction, database user management, data lineage analysis, and column-level data profiling.
    39
    49 npm
    124 PyPI
    48
    MIT
  • A
    license
    Not graded
    quality
    F
    maintenance
    Enables AI assistants to manage SAP Cloud Integration (CPI) landscapes through natural language by exposing CPI OData APIs as MCP tools.
    12
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    A config-driven MCP server that exposes OData and REST APIs as MCP tools, enabling AI assistants to query, manage, and monitor SAP backends through natural language.
    112 npm
    32
    MIT