---
title: MCP Quickstart
description: Connect your MCP client to the SAP Developers hosted MCP server.
---
# MCP Quickstart
The SAP Developers site exposes a hosted **Model Context Protocol (MCP)** server so your AI client — Claude Desktop, Claude Code, or any MCP-compatible tool — can search tutorials, read missions, and query the knowledge graph directly. No SDK, no scraping.
Phase 1 is **anonymous and read-only**. There's no sign-in and no API key. The approuter's per-IP throttle still applies, so treat it as a shared resource.
## Available services
Three CDS services are mounted under `/mcp/*` using `@cap-js/mcp@1.1.1` over the **Streamable HTTP** transport (MCP protocol version 2025-06). Each service exposes two auto-generated tools (`describe`, `query`) plus one tool per CDS action.
| Service | Mount | Curated tools |
| --- | --- | --- |
| SearchService | `/mcp/search` | `search_tutorials`, `list_missions`, `get_mission`, `get_tutorial`, `search_events`, `search_channels` |
| HomepageService | `/mcp/homepage` | `get_recent_news`, `get_recent_videos`, `get_news_detail` |
| KnowledgeGraphService | `/mcp/graph` | `kg_prerequisites`, `kg_what_to_learn_next` |
One-liners:
- **`search_tutorials`** — full-text search across published tutorials.
- **`list_missions`** — enumerate curated learning missions.
- **`get_mission`** — fetch a mission's ordered tutorial list by slug.
- **`get_tutorial`** — fetch a single tutorial's metadata and rendered steps.
- **`search_events`** — search the public SAP community events catalog (CodeJams, Devtoberfest, TechEd, user groups) by text, type, and region.
- **`search_channels`** — search the public external-channels catalog (SAP and community YouTube channels, blogs, podcasts, feeds) by text, category, platform, and owner scope.
- **`get_recent_news`** — recent SAP Developer News episodes shown on the homepage.
- **`get_recent_videos`** — recent SAP Developers YouTube videos shown on the homepage.
- **`get_news_detail`** — full article body for one news item, fetched by its URL (complements `get_recent_news`).
- **`kg_prerequisites`** — concepts you should already know before this tutorial.
- **`kg_what_to_learn_next`** — recommended follow-on tutorials from the knowledge graph.
Full parameter reference: [docs/developers/reference/mcp-server.md](../developers/reference/mcp-server.md).
## Base URLs
- **Production:** `https://developers.sap.com` (cutover end of July 2026)
- **Dev:** ask your admin for the current dev route
Replace `` in the examples below with the appropriate URL.
## Claude Desktop
Edit `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/`, Windows: `%APPDATA%\Claude\`):
```json
{
"mcpServers": {
"sap-developers-search": {
"url": "/mcp/search"
},
"sap-developers-homepage": {
"url": "/mcp/homepage"
},
"sap-developers-graph": {
"url": "/mcp/graph"
}
}
}
```
Restart Claude Desktop. The tools appear in the tools picker.
## Claude Code
Add a `.mcp.json` at the root of your project:
```json
{
"mcpServers": {
"sap-developers-search": {
"type": "http",
"url": "/mcp/search"
},
"sap-developers-homepage": {
"type": "http",
"url": "/mcp/homepage"
},
"sap-developers-graph": {
"type": "http",
"url": "/mcp/graph"
}
}
}
```
## Joule Work Desktop
> **Naming:** SAP ships no product literally called "Joule Studio Desktop." The desktop client
> that connects to a remote MCP server by pasting a URL is **Joule Work Desktop** (GA expected
> Q3–Q4 2026). If you meant the cloud low-code agent builder **Joule Studio, classic edition**,
> see [Joule Studio (classic edition)](#joule-studio-classic-edition) below — it connects via a
> BTP destination, not a URL.
Joule Work Desktop connects to an MCP server by registering the server's URL. For our
**anonymous, read-only Phase 1** services this is all you need — no sign-in, no destination:
1. In Joule Work Desktop, add / register an MCP server.
2. Paste the service URL, e.g. `/mcp/search` (the server speaks Streamable HTTP, which
Joule Work Desktop expects).
3. Repeat for `/mcp/homepage` and `/mcp/graph` as needed.
The curated tools then appear to Joule.
> **Authenticated tools (`/mcp-auth/*`) work from Joule Work Desktop's native flow.**
> Pasting an authenticated URL triggers Joule Work Desktop's OAuth 2.0 handshake, which uses
> **Dynamic Client Registration (RFC 7591)** — it `POST`s to the `registration_endpoint` to
> self-register. The server implements DCR (facade over the environment's IAS public client),
> so Joule Work Desktop self-registers, shows the Application Access Request consent, and the
> personalized tools (`get_my_tutorials`, `complete_step`, …) become available after sign-in.
## Joule Studio (classic edition)
If instead you are building a **Joule agent** in the cloud low-code **Joule Studio, classic
edition**, MCP servers are added via an **SAP BTP destination**, not a raw URL. Only the
anonymous Phase 1 `/mcp/*` services fit cleanly (the authenticated namespaces need a
pre-registered OAuth client, and interactive/authorization-code OAuth is not supported here).
1. In **SAP BTP Cockpit**, create a **streamable HTTPS** destination whose URL points at
`` (the destination URL **must not** end in `/mcp`), with the additional property
**`sap-joule-studio-mcp-server = true`**. Create it with the **same name** in both the Joule
Studio classic subaccount and the Joule subaccount, and register it in the control tower.
2. In your Joule agent, open the **MCP Servers** tab → **Add MCP Server**.
3. Give it a **Name** and **Description**, set **Path** to the service you want (e.g.
`/mcp/search`), pick the destination, review the listed **Tools**, and choose **Add Server**.
SSE-type destinations are **not** supported. For servers that do require auth, SAP recommends
**Client Credentials**; **OAuth2 Authorization Code** and **Principal Propagation** are not
supported. Full reference: [Add MCP Servers to Your Joule Agent](https://help.sap.com/docs/joule-studio-classic/joule-studio-classic-edition/add-mcp-servers-to-your-joule-agent).
## Older stdio-only clients
Clients that only speak stdio (older Claude Desktop builds, custom scripts) can bridge through **`mcp-remote`**:
```bash
npm install -g mcp-remote
```
Then in your client config:
```json
{
"mcpServers": {
"sap-developers-search": {
"command": "npx",
"args": ["-y", "mcp-remote", "/mcp/search"]
}
}
}
```
Repeat the block for `/mcp/homepage` and `/mcp/graph` as needed.
## Transport notes
The server speaks Streamable HTTP. Clients may send `Accept: application/json` or `Accept: text/event-stream`. JSON-only clients get JSON responses; SSE-capable clients get streaming responses when appropriate. You don't need to configure this — MCP clients negotiate it.
## Troubleshooting
- **`initialize` returns 401.** You hit `/mcp-auth/*`, the **authenticated** surface — it needs an OAuth bearer (IAS/XSUAA). For the anonymous tools use `/mcp/*` (`/mcp/search`, `/mcp/homepage`, `/mcp/graph`).
- **`tools/list` returns an empty array.** Wrong service path. Each service is mounted separately — `/mcp/search`, `/mcp/homepage`, `/mcp/graph`. There is no aggregate `/mcp` root.
- **First response is slow (5–15s).** Cold start. The CAP backend spins down when idle. Subsequent requests are fast.
- **Nothing returns and no error.** Anonymous IP throttle. Back off and retry.
## Sign in with Claude Desktop (OAuth)
Phase 2 adds authenticated tools under `/mcp-auth/*` — your tutorial progress, events, and personalized recommendations. To use them, sign in with your SAP universal ID via OAuth.
> **Native-HTTP OAuth clients (incl. Claude Desktop) connect with NO client_id.**
> The server supports **RFC 7591 Dynamic Client Registration**: a client that does its own
> OAuth discovers the `registration_endpoint` from
> `/.well-known/oauth-authorization-server`, self-registers, and is handed the correct
> client_id for that environment automatically — just point it at `/mcp-auth/api` and
> approve the browser sign-in. No `--static-oauth-client-info`, no per-environment id to
> look up. Use the `mcp-remote` bridge below **only** for clients that cannot do native HTTP
> OAuth; it needs the explicit, environment-specific `client_id` from the table.
For clients that need the `mcp-remote` bridge (no native OAuth), supply the environment's `client_id`:
```json
{
"mcpServers": {
"sap-developers-auth": {
"command": "npx",
"args": [
"-y", "mcp-remote", "/mcp-auth/api",
"--static-oauth-client-info", "{\"client_id\":\"5a7cb5fd-a0f0-4678-8f51-f8c471e0ab34\"}",
"--host", "localhost"
]
}
}
}
```
> **The `client_id` is ENVIRONMENT-SPECIFIC — match it to your ``:**
>
> | Environment | `` | `client_id` |
> | --- | --- | --- |
> | **Production** | `https://developers.sap.com` | `5a7cb5fd-a0f0-4678-8f51-f8c471e0ab34` |
> | **Dev** | your dev route | `0b1e8b56-5f5d-4ebf-a9e9-28aae2964236` |
>
> Both environments authenticate against **IAS** (a public OIDC client —
> authorization_code + PKCE S256, no secret) in the shared tenant `atxgsg7zi`. But DEV
> and PROD each have their **own** `tutorials-identity` service instance, and each
> instance has a **distinct clientid** — so the ids differ per environment. The
> interactive id_token's `aud` equals the client_id you supply, and the resource
> (`/mcp-auth`) validates it against the clientid bound to that env's `tutorials-srv-mcp`
> — using the wrong env's id fails with a `WrongAudience`/`invalid_token` 401 **after** a
> successful browser login ("server rejected a token it just issued"). The old XSUAA ids
> (`sb-tutorials-prod!…`, `sb-tutorials!…`) are **retired**: since `MCP_ISSUER_KIND: ias`
> landed in both mtaexts (#2593), discovery advertises IAS and an XSUAA `sb-…` id is
> rejected at the authorize endpoint. If a `tutorials-identity` instance is recreated its
> clientid changes — read the current value from the IAS admin console (tenant
> `atxgsg7zi` → app `tutorials-identity` for that space → Client Authentication → Client
> ID), or from `VCAP_SERVICES.identity[].credentials.clientid` on that env's
> `tutorials-srv-mcp`, and update the matching row.
On first connection `mcp-remote` opens a browser tab for consent (PKCE, no client secret required). The endpoints are discovered automatically from `/.well-known/oauth-authorization-server`; you supply only the `client_id`. After approval, the token is cached and refreshed silently.
**Available authenticated tools** (DeveloperService + HomepageService):
| Tool | What it does |
| --- | --- |
| `get_my_tutorials` | Your in-progress and completed tutorials |
| `get_my_missions` | Your mission progress |
| `get_my_events` | Your registered upcoming events |
| `get_my_completed_steps` | Completed step numbers for a specific tutorial |
| `get_tutorial_step` | Full HTML content of a tutorial step |
| `complete_step` | Mark a step done |
| `reset_tutorial_progress` | Reset all progress on a tutorial |
| `get_my_recommended_tutorials` | Persona-ranked tutorial recommendations |
| `get_my_recommended_missions` | Persona-ranked mission recommendations |
> **Mint a PAT:** open [`/me/tokens/`](/me/tokens/) (self-service — any signed-in user with the **Tutorials MCP Users** role collection) and use **Create token**. The token is shown once at mint time — copy it immediately.
## Sign in with Claude Code (OAuth via mcp-remote)
> **Claude Code's *native* `type: http` OAuth now works against this server.** Its built-in
> OAuth client uses **Dynamic Client Registration** (RFC 7591) — it `POST`s to the
> `registration_endpoint` to self-register. The server implements DCR, so native HTTP OAuth
> succeeds with **no** `client_id`: `"sap-developers-auth": { "type": "http", "url":
> "/mcp-auth/api" }`, then approve the browser sign-in. The `mcp-remote` bridge below
> remains available for older clients or headless use; a **[PAT](#headless--ci-with-a-personal-access-token)**
> is still simplest for CI (no browser flow).
For older clients without native OAuth, bridge through `mcp-remote`, which performs the OAuth
2.1 authorization-code + PKCE handshake and forwards the bearer to `/mcp-auth/api` (supply the
environment's `client_id` from the table above):
```bash
npm install -g mcp-remote
```
```json
{
"mcpServers": {
"sap-developers-auth": {
"command": "npx",
"args": [
"-y", "mcp-remote", "/mcp-auth/api",
"--static-oauth-client-info", "{\"client_id\":\"5a7cb5fd-a0f0-4678-8f51-f8c471e0ab34\"}",
"--host", "localhost"
]
}
}
}
```
`5a7cb5fd-a0f0-4678-8f51-f8c471e0ab34` (PROD) / `0b1e8b56-5f5d-4ebf-a9e9-28aae2964236` (DEV)
is the **IAS public OIDC client** (`tutorials-identity` app) for each environment — a public
PKCE client with no secret, in the shared IAS tenant `atxgsg7zi`. DEV and PROD each have their
**own** `tutorials-identity` service instance with a **distinct clientid**, so you must use the id
matching your `` (see the table above) — the resource validates the token's `aud` against
its own env's bound clientid. The retired XSUAA ids (`sb-tutorials-prod!…`, `sb-tutorials!…`) no
longer work — IAS rejects them at the authorize endpoint. To confirm the current id for an env,
read it from the IAS admin console (tenant `atxgsg7zi`, the `tutorials-identity` app for that space
→ Client Authentication → Client ID) or from `VCAP_SERVICES.identity[].credentials.clientid` on
that env's `tutorials-srv-mcp`.
The flow uses PKCE with no client secret. On first run, `mcp-remote` opens your browser for the
SAP universal-ID consent flow; after approval the token is cached in `~/.mcp-auth/` and refreshed
silently. The server advertises its endpoints at `/.well-known/oauth-authorization-server`,
so `mcp-remote` discovers the authorize/token URLs automatically — you only supply the `client_id`.
> **Flag note:** use `--static-oauth-client-info '{"client_id":"…"}'`, **not**
> `--static-oauth-client-id`. The latter is not a real `mcp-remote` flag — it is silently
> ignored, so `mcp-remote` falls back to Dynamic Client Registration and fails with
> `does not support dynamic client registration`. Only `--static-oauth-client-info` (a JSON
> blob carrying `client_id`) short-circuits registration.
> **Callback-host note (`--host localhost`):** XSUAA matches the OAuth `redirect_uri`
> literally against the client's registered list, which whitelists
> `http://localhost:*/oauth/callback`. Some `mcp-remote` builds bind the callback to the
> loopback **IP** `127.0.0.1` instead (RFC 8252's preferred default), producing
> `http://127.0.0.1:/oauth/callback` — which does **not** match `localhost` and fails
> at `/oauth/authorize` with **"redirect_uri does not match the configuration."** Passing
> `--host localhost` forces the registered hostname to match. If you still hit this, delete
> the cached tokens in `~/.mcp-auth/` and reconnect.
> **Simplest path for Claude Code:** skip OAuth entirely and use a
> [Personal Access Token](#headless--ci-with-a-personal-access-token). The PAT path needs no
> browser handshake and no pre-registered client.
## Headless / CI with a Personal Access Token
For CI pipelines, scripts, and headless agents that cannot complete an interactive OAuth flow, use a Personal Access Token (PAT).
**Mint a PAT:**
1. Sign in to `/me/tokens/` as a user with the `Tutorials MCP Users` role collection.
2. Click **New token**, give it a name, select scopes (`read` for read-only tools; `read write` to allow `complete_step` and `reset_tutorial_progress`), set a TTL.
3. Copy the displayed token — it is shown once only. The server stores only a SHA-256 hash.
**Configure your MCP client:**
```json
{
"mcpServers": {
"sap-developers-auth": {
"url": "/mcp-pat/api",
"headers": {
"Authorization": "Bearer pat_..."
}
}
}
}
```
PATs are validated by the `mcp-pat-middleware` in the CAP backend. An expired or revoked PAT returns 401.
## Reading tutorial content as resources (Phase 3)
MCP clients can read tutorials, missions, and concepts as first-class resources:
- `tutorial://` — a tutorial's metadata, step titles, and rendered HTML.
- `mission://` — a mission and its ordered tutorials.
- `concept://` — a knowledge-graph concept and the tutorials that teach it.
List them with `resources/list`; read one with `resources/read` and the URI. Example (Claude Desktop): just ask "read tutorial://hcp-create-trial-account and summarize step 2".
## Prompt templates (Phase 3)
The server ships reusable prompt templates, discoverable via `prompts/list`:
| Prompt | Arguments | What it does |
|---|---|---|
| `summarize_mission_for_beginner` | `mission_slug` | Beginner-friendly mission summary |
| `generate_lab_exercise` | `tutorial_slug`, `step?` | A hands-on lab from a tutorial |
| `explain_concept` | `concept_id` | Explains a KG concept and its tutorials |
| `suggest_learning_path` | `from_slug`, `to_slug` | Ordered path between two tutorials |
Invoke with `prompts/get`; the client fills the arguments.
## What's next
Phase 3 is shipping. The `/mcp/*`, `/mcp-auth/*`, and `/mcp-pat/*` namespaces are stable — client configs will not need to change.