--- 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.