SAP Joule — S/4HANA Cloud Procurement & Finance CopilotBLOCK
Reference implementation: SAP Joule procurement copilot backed by a CAP AI service and an LLM-agnostic plugin (Anthropic / Ollama / SAP Generative AI Hub).
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
A real SAP Joule project targeting S/4HANA Cloud for Finance, Procurement, and Supply Chain. Includes deployable Joule Studio artifacts (skills, agent, actions, destinations) and a customer-facing pitch site.
Use this repo to:
- Spin up a free SAP BTP trial with Joule Studio enabled (see
docs/01-setup-btp-trial.md). - Import the skills and agent from
joule-project/into Joule Studio. - Run the live demo using
docs/04-demo-script.md. - Show the pitch site (
pitch-site/) as the visual companion during the customer meeting.
What's inside
The five skills
The agent
procurement-copilot is a Joule Agent that composes the skills into multi-step conversations, e.g.:
"Check MAT-1001 stock in plant
eac8e60fd788OBSERVED · 2026-10-12Connect
Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.
claude mcp add joule-pitch-site --env AICORE_AUTH_URL=${AICORE_AUTH_URL} --env AICORE_CLIENT_SECRET=${AICORE_CLIENT_SECRET} --env ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} --env GEMINI_API_KEY=${GEMINI_API_KEY} -- npx -y [email protected]{
"mcpServers": {
"joule-pitch-site": {
"command": "npx",
"args": [
"-y",
"[email protected]"
],
"env": {
"AICORE_AUTH_URL": "${AICORE_AUTH_URL}",
"AICORE_CLIENT_SECRET": "${AICORE_CLIENT_SECRET}",
"ANTHROPIC_API_KEY": "${ANTHROPIC_API_KEY}",
"GEMINI_API_KEY": "${GEMINI_API_KEY}"
}
}
}
}Exposed tools (70)
66 read · 2 write · 2 destructive. Blast radius: 2 tools can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.
| Tool | Risk | Description |
|---|---|---|
A | read | first |
Acme | read | Global widgets. |
B | read | beta |
C | read | gamma |
Doomed | read | x |
Kept | read | x |
One | read | a |
Original | read | text |
Paper | read | sheets |
Shipping | read | Free over 50 EUR. |
Steel | read | coils |
Test | read | x |
Three | read | c |
Two | read | b |
Updated | read | text |
X | read | y |
a | read | x |
accuracy | read | facts match source |
add | write | add two numbers |
b | read | |
bad | read | always throws |
boom | read | always fails |
boom-agent | read | x |
brevity | read | 2 sentences max |
c | read | |
classify | write | Classify input into one of a fixed set of labels. |
compliance | read | Compliance checker. |
compliance-checker | read | Flags compliance concerns (REACH, RoHS, GDPR, sanctions, green claims) in a contract or scenario. |
consult | read | |
contract-lookup | read | Answers questions about supplier contracts. |
default | read | answer must cite a source |
echo | read | echo the input |
echo-agent | read | Echoes the question back. |
extract_json | read | Extract structured JSON from unstructured text against a given JSON schema. |
get_weather | read | get w |
greet | read | |
greet_from_file | read | file-based prompt |
grounding | read | cites specific IDs |
labels | read | Array of allowed labels |
list_providers | read | List every provider kind this plugin supports, the active default, and any configured aliases. Aliases are pickable via the |
lookup | read | look up |
ls | read | |
my_custom_from_disk | read | loaded via --prompts-dir |
my_prompt | read | a demo |
p | read | |
p1 | read | demo prompt |
peek | read | |
plain | read | plain agent |
price | read | Price analyst. |
price-analyst | read | prices |
procurement_risk_scorer | read | Score procurement / contract text for risk (SAP-flavored built-in prompt for demos). |
reload_budget | read | Re-read LlmBudget rows into the costBudget middleware. Call after editing rows via OData. |
reset_cache | destructive | Clear both the exact-match cache and the semantic index. Use to force cache warmup after a data change. |
reset_injection_stats | destructive | Reset the promptInjectionGuard counters (scanned/blocked/sanitized/warned + per-detector). Cadence: after ops reviews the pattern of hits. |
schema | read | JSON schema (object) for the output |
search | read | search things |
search_contracts | read | Semantic + keyword search over supplier contracts. Returns the top matching contracts as JSON. |
sentences | read | Target length |
slow | read | reports progress |
sql-worker | read | Runs SQL against the analytics warehouse. |
summarize | read | Summarize input text in a target number of sentences. |
t | read | |
targetLanguage | read | Target language (e.g. |
test | read | test |
text | read | Text to summarize |
translate | read | Translate text into a target language. |
unused_tool | read | A tool the model should not call for this prompt. |
verify | read | Sanity-check a configured provider by sending a tiny probe. |
with-tools | read | x |
x | read | x |
Trust audit
BLOCKgrade F · trust 28/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | WARN |
| L1 | Static analysis of the code | FAIL |
| L2 | Instruction surface (what it tells the agent) | FAIL |
| L3 | Class-specific surface | WARN |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- declared (6 observation(s))
- Network
- declared (10 observation(s))
- Shell
- declared (3 observation(s))
- Dependencies
- not all pinned
- Secrets in source
- found
Findings (25)
eval(script: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
throw new Error('redisRateLimit: options.redis is required and must expose an eval() method');const ZERO_WIDTH_RE = /[---]/g;
- **Injection pattern detection** (`DEFAULT_INJECTION_PATTERNS`) catches the common jailbreak seeds accidentally left in prompts:
| `promptInjectionGuard` (1.31) | Blocks jailbreak / role-marker patterns |
- `skipMethods: ['embed']` — bypass for methods where safety doesn't apply
cds-plugin-llm-and-vector-hana.pptx
console.log(`\n\n\x1b[2mfirst token: ${first}ms · total: ${total}ms · ${chunk.usage?.output_tokens ?? '?'} output tokens\x1b[0m`);saptarishi-llm verify --provider ollama --base-url http://192.168.5.13:11434
const ZERO_WIDTH_RE = /[---]/g;
ANTHROPIC_API_KEY: 'sk-ant-api03-abcdefghijklmnop-secret',
credentials: { apiKey: 'sk-ant-fake-for-init-only' },const SECRET = 'test-response-signing-key';
reset_cache, reset_injection_stats
.cfignore
return crypto.createHash('sha1').update(stable).digest('hex');const { waitForBatch } = require('../../batchHelpers');const { chainDiff, formatChainDiff } = require('../../chainDiff');const { validateMiddlewareOrder } = require('../../validateMiddlewareOrder');const { DEFAULT_PRICING } = require('../../pricing');const { getTokenizer, charsPerTokenFor } = require('../../tokenizer');OLLAMA_URL=http://192.168.5.13:11434 saptarishi-llm chat --provider ollama -p "hello"
# → MCP HTTP+SSE listening on http://0.0.0.0:3333/sse
credentials: { apiKey: 'sk-fake', baseUrl: 'http://127.0.0.1:65535/v1' },credentials: { apiKey: 'sk-fake', baseUrl: 'http://127.0.0.1:65535/v1' },Gates applied: instruction_override, no_behavioural_pass.
eac8e60fd788full audit observations/trust-audit/mcp-server/kalyanjanumpally__sap-joule-ai-plugin.json · Report an issue / request a re-scanAudit history
Every audit this server has had. A grade with a past is a grade somebody is still checking.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-12 | eac8e60fd788 | BLOCK | F | 28 | first audit |
Questions
What is the @saptarishi/cds-plugin-llm MCP server?
Reference implementation: SAP Joule procurement copilot backed by a CAP AI service and an LLM-agnostic plugin (Anthropic / Ollama / SAP Generative AI Hub).
What tools does @saptarishi/cds-plugin-llm expose?
70 in total: 66 read-only, 2 that write, and 2 that can delete or overwrite (reset_cache, reset_injection_stats). Every one is listed on this page with its risk.
Is @saptarishi/cds-plugin-llm safe to connect to an agent?
No — not without reading the findings first. The audit graded it F (28/100) and found 6 critical or high issues in the source. Each one is listed on this page with the file and line it is on. Separately from the audit: 2 of its tools can destroy data, so scope the token you give it to what you actually need.
What credentials does @saptarishi/cds-plugin-llm need?
It reads AICORE_AUTH_URL, AICORE_CLIENT_SECRET, ANTHROPIC_API_KEY, GEMINI_API_KEY, GOOGLE_API_KEY, GROQ_API_KEY, LLM_HMAC_KEY, LLM_RESPONSE_HMAC_KEY, MCP_OBS_TOKEN, OPENAI_API_KEY, OPENAI_KEY_A and OPENAI_KEY_B from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.
How does @saptarishi/cds-plugin-llm run?
It speaks streamable-http, so it runs as a service you connect to over the network. It is published on npm as joule-pitch-site at 1.0.0.
How current is this page?
The grade is for one exact copy of the source (eac8e60fd788), read on 2026-10-12. The repository is watched and re-audited when it changes.