Docker Guide for arc1¶
arc1 ships as a Docker image (and npm package) that speaks MCP over HTTP streamable (the default transport in the Docker image). This makes it easy to run as a long-lived, port-accessible container that multiple MCP clients can connect to without spawning a new process per session.
stdio mode is still supported. Pass
-e SAP_TRANSPORT=stdioand usedocker run -ito revert to the classic pipe-based transport.
Table of Contents¶
- Quick Start
- Pre-Built Images (GHCR)
- Building the Image
- How arc1 Runs in Docker
- Configuration Reference
- Connection
- Authentication
- Safety / Read-Only
- Transport Management
- Feature Flags
- Debugger
- Network / TLS
- MCP Client Integration
- Claude Desktop
- Gemini CLI / Other Agents
- Common Configurations
- Read-Only for Production Review
- Sandboxed AI (Z* packages only)
- Cookie Authentication
- Corporate Proxy
- Updating the Image
- Security Notes
- Troubleshooting
Quick Start¶
No Docker Hub needed. Pre-built images are published automatically to GitHub Container Registry (GHCR) on every release. Pull them with
docker pull ghcr.io/marianfoo/arc-1:latest.
HTTP streamable (default — recommended)¶
The Docker image defaults to SAP_TRANSPORT=http-streamable listening on
0.0.0.0:8080. Start the container, map the port, and connect any MCP client
to http://localhost:8080/mcp.
# Start arc1 as a persistent HTTP MCP server
docker run -d --rm \
-p 8080:8080 \
-e SAP_URL=https://host:44300 \
-e SAP_USER=developer \
-e SAP_PASSWORD=secret \
ghcr.io/marianfoo/arc-1:latest
# Verify it is up
curl -s http://localhost:8080/mcp # should return an MCP protocol response
stdio mode (classic, pipe-based)¶
docker run -i --rm \
-e SAP_URL=https://host:44300 \
-e SAP_USER=developer \
-e SAP_PASSWORD=secret \
-e SAP_TRANSPORT=stdio \
ghcr.io/marianfoo/arc-1:latest
-iis required for stdio mode. MCP communicates over stdin/stdout. Without-ithe container exits immediately because stdin is closed.
Pre-Built Images (GHCR)¶
Official images are built automatically by GitHub Actions and pushed to GitHub Container Registry — no Docker Hub account is required for either pulling or publishing.
Image location¶
Available tags¶
| Tag | Example | Description |
|---|---|---|
latest |
ghcr.io/marianfoo/arc-1:latest |
Updated on every push to main (dev builds) and on every release |
x.y.z |
ghcr.io/marianfoo/arc-1:0.2.0 |
Exact version (immutable, created on release) |
x.y |
ghcr.io/marianfoo/arc-1:0.2 |
Latest patch within minor (created on release) |
latest is rebuilt on every push to main, so it always reflects the newest code — even unreleased changes. Use versioned tags for production.
Pulling¶
# Latest (includes unreleased changes from main)
docker pull ghcr.io/marianfoo/arc-1:latest
# Pinned version (recommended for production/team use)
docker pull ghcr.io/marianfoo/arc-1:0.2.0
Supported platforms¶
Each image is a multi-platform manifest covering:
| Platform | Architecture |
|---|---|
linux/amd64 |
x86-64 servers, most CI runners |
linux/arm64 |
Apple Silicon (via Rosetta or native Linux VM), AWS Graviton |
Docker automatically selects the right variant for your host.
GitHub Actions: automated publishing¶
The workflow .github/workflows/docker.yml triggers on every v* tag push
(the same event fired at the end of the release.yml workflow):
release.yml
└─► git tag v2.22.0 + git push tag
│
└─► docker.yml triggered
├── docker/setup-qemu-action (arm64 emulation)
├── docker/setup-buildx-action (multi-platform builder)
├── docker/login-action (GITHUB_TOKEN → ghcr.io)
├── docker/metadata-action (semver tags + OCI labels)
└── docker/build-push-action (build + push)
No extra secrets are needed. The workflow uses the built-in GITHUB_TOKEN
with packages: write permission. This token is automatically available in all
GitHub Actions runs.
Manual re-publish (workflow_dispatch)¶
You can trigger a new Docker build for any existing tag without a full release:
- Go to Actions → Docker in the GitHub UI.
- Click Run workflow.
- Optionally supply an image tag override.
This is useful for republishing after a Dockerfile fix without bumping the
Go version.
Visibility¶
Packages on GHCR inherit the visibility of the repository. If the repository is
public, the images are public and can be pulled anonymously. If private,
authentication is required (docker login ghcr.io).
Building the Image¶
From source¶
# Simple build (version = dev)
docker build -t arc1 .
# With version metadata (recommended for releases)
docker build \
--build-arg VERSION=2.22.0 \
--build-arg COMMIT=$(git rev-parse --short HEAD) \
--build-arg BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
-t arc1:2.22.0 .
Multi-platform build (for sharing)¶
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg VERSION=2.22.0 \
-t ghcr.io/yourorg/arc1:2.22.0 \
--push .
Build note: The image uses
node:20-alpineas builder. The runtime image isnode:20-alpinewith only production dependencies installed.better-sqlite3requires native compilation duringnpm install.
How arc1 Runs in Docker¶
HTTP streamable (default)¶
The Docker image defaults to the MCP streamable HTTP transport, listening on
0.0.0.0:8080. This is the recommended mode for containerised deployments:
MCP Client (Claude Desktop, Cursor, etc.)
│
│ HTTP POST http://localhost:8080/mcp
│
├─► docker container (long-lived, -d)
│ │
│ JSON-RPC over HTTP (streaming)
│ │
└─────────┴─► container keeps running; multiple clients can connect
Key differences from stdio mode:
- Port 8080 is exposed — map it with -p 8080:8080.
- -d (detached) mode works — the container stays alive between sessions.
- No -i flag needed — stdin is not used.
- Multiple clients can connect to the same container simultaneously.
stdio mode (classic)¶
Set SAP_TRANSPORT=stdio to revert to the pipe-based model where an MCP client
spawns the container as a subprocess:
MCP Client
│
├─► docker run -i --rm -e SAP_TRANSPORT=stdio -e SAP_URL=... arc1
│ │
│ JSON-RPC over stdin/stdout
│ │
└─────────┴─► container exits when client disconnects
Transport / address options¶
| Env variable | CLI flag | Default in image | Description |
|---|---|---|---|
SAP_TRANSPORT |
--transport |
http-streamable |
stdio or http-streamable |
SAP_HTTP_ADDR |
--http-addr |
0.0.0.0:8080 |
Listen address for http-streamable |
Configuration Reference¶
All configuration is done through environment variables with the SAP_
prefix. CLI flags map 1:1 to env vars:
| CLI flag | Env variable | Default |
|---|---|---|
--url |
SAP_URL |
(required) |
--user |
SAP_USER |
|
--password |
SAP_PASSWORD |
|
--client |
SAP_CLIENT |
001 |
--language |
SAP_LANGUAGE |
EN |
--insecure |
SAP_INSECURE |
false |
--read-only |
SAP_READ_ONLY |
false |
--block-free-sql |
SAP_BLOCK_FREE_SQL |
false |
--allowed-ops |
SAP_ALLOWED_OPS |
|
--disallowed-ops |
SAP_DISALLOWED_OPS |
|
--allowed-packages |
SAP_ALLOWED_PACKAGES |
|
--allow-transportable-edits |
SAP_ALLOW_TRANSPORTABLE_EDITS |
false |
--enable-transports |
SAP_ENABLE_TRANSPORTS |
false |
--transport-read-only |
SAP_TRANSPORT_READ_ONLY |
false |
--allowed-transports |
SAP_ALLOWED_TRANSPORTS |
|
--feature-abapgit |
SAP_FEATURE_ABAPGIT |
auto |
--feature-rap |
SAP_FEATURE_RAP |
auto |
--feature-amdp |
SAP_FEATURE_AMDP |
auto |
--feature-ui5 |
SAP_FEATURE_UI5 |
auto |
--feature-transport |
SAP_FEATURE_TRANSPORT |
auto |
--feature-hana |
SAP_FEATURE_HANA |
auto |
--transport |
SAP_TRANSPORT |
http-streamable (image default) |
--http-addr |
SAP_HTTP_ADDR |
0.0.0.0:8080 (image default) |
--terminal-id |
SAP_TERMINAL_ID |
|
--verbose |
SAP_VERBOSE |
false |
| MCP Client Auth | ||
--api-key |
ARC1_API_KEY |
|
--oidc-issuer |
SAP_OIDC_ISSUER |
|
--oidc-audience |
SAP_OIDC_AUDIENCE |
|
--oidc-username-claim |
SAP_OIDC_USERNAME_CLAIM |
preferred_username |
--oidc-user-mapping |
SAP_OIDC_USER_MAPPING |
|
--pp-ca-key |
SAP_PP_CA_KEY |
|
--pp-ca-cert |
SAP_PP_CA_CERT |
|
--pp-cert-ttl |
SAP_PP_CERT_TTL |
5m |
Connection¶
SAP_URL=https://host:44300 # Required. Include scheme and port.
SAP_CLIENT=001 # SAP client number (3 digits)
SAP_LANGUAGE=EN # ABAP session language (2-char ISO)
Authentication¶
arc1 supports multiple auth methods for connecting to SAP, plus optional MCP client authentication.
MCP Client Authentication (Hop 1: Client → arc1)¶
When running arc1 as a shared server, protect it with API key or OAuth:
# API Key (simplest)
-e ARC1_API_KEY='your-secret-key'
# OAuth/OIDC JWT validation (enterprise)
-e SAP_OIDC_ISSUER='https://login.microsoftonline.com/{tenant}/v2.0' \
-e SAP_OIDC_AUDIENCE='api://arc1-sap-connector'
# Principal Propagation (per-user SAP auth, requires OIDC)
-e SAP_PP_CA_KEY=/secrets/ca.key \
-e SAP_PP_CA_CERT=/secrets/ca.crt \
-v /path/to/secrets:/secrets:ro
See the Authentication guides for detailed setup.
SAP Authentication (Hop 2: arc1 → SAP)¶
arc1 supports these mutually exclusive SAP auth methods. Use exactly one.
Basic auth (username + password)¶
Cookie file (Netscape format)¶
Mount a cookie file into the container and reference it:
docker run -i --rm \
-e SAP_URL=https://host:44300 \
-e SAP_COOKIE_FILE=/cookies/cookies.txt \
-v /path/to/local/cookies.txt:/cookies/cookies.txt:ro \
arc1
The cookie file must use the Netscape format exported by browser extensions like Edit This Cookie or Cookie-Editor.
Cookie string¶
Never bake credentials into the image with
ENVin a downstream Dockerfile. Always pass them at runtime via-eor--env-file.
Using --env-file¶
Keep credentials in a local .env file (never committed to git):
Safety / Read-Only¶
These flags protect SAP systems from unintended AI-driven modifications.
Full read-only (safest)¶
Blocks all write operations — create, update, delete, activate, execute:
Use this when pointing an AI at a production or QA system where you only want read/search access.
Block free SQL¶
Prevents the AI from running arbitrary SELECT statements via RunQuery:
Operation allowlist¶
Only permit specific operation types. The operation codes are:
| Code | Meaning |
|---|---|
R |
Read (GetSource, GetObject, etc.) |
S |
Search (SearchObjects, GrepSource, etc.) |
Q |
Query (RunQuery — free SQL) |
C |
Create |
D |
Delete |
U |
Update (EditSource, WriteSource) |
A |
Activate |
Example — allow only read and search (equivalent to read-only but explicit):
Example — allow read, search, and query but no writes:
Operation blocklist¶
Block specific operation types while allowing everything else:
SAP_ALLOWED_OPSandSAP_DISALLOWED_OPSare mutually exclusive. Use one or the other, not both.
Package restriction¶
Restrict all write operations to specific packages. Supports wildcards:
# Only allow writes to custom packages and $TMP
-e SAP_ALLOWED_PACKAGES="Z*,$TMP"
# Only one specific package
-e SAP_ALLOWED_PACKAGES="\$ZRAY_DEV"
# Multiple packages (comma-separated)
-e SAP_ALLOWED_PACKAGES="ZMYAPP,ZMYAPP_TEST,\$TMP"
Read operations (R, S) are not restricted by this filter — the AI can still
read objects from any package; it just cannot modify objects outside the
allowlist.
Transportable edits¶
By default, arc1 only allows editing objects in local (non-transportable) packages
like $TMP. To allow edits to objects that require a transport request:
When enabled, write tools accept an optional transport parameter. Without a
transport number, the edit will still fail for transportable objects.
Transport Management¶
CTS transport tools are disabled by default to prevent accidental releases.
# Enable transport tools
-e SAP_ENABLE_TRANSPORTS=true
# Enable but restrict to read-only (list, view — no create/release/delete)
-e SAP_ENABLE_TRANSPORTS=true
-e SAP_TRANSPORT_READ_ONLY=true
# Enable and restrict to specific transports (wildcards OK)
-e SAP_ENABLE_TRANSPORTS=true
-e SAP_ALLOWED_TRANSPORTS="A4HK*"
Feature Flags¶
Feature flags control whether optional SAP features are enabled. The default
auto mode probes the SAP system on startup and enables a feature only if the
corresponding ABAP components are installed.
Use on or off to skip the probe and force the behavior:
| Variable | Feature | Notes |
|---|---|---|
SAP_FEATURE_RAP |
RAP/OData development | BDEF, SRVD, SRVB tools |
SAP_FEATURE_AMDP |
AMDP/HANA debugger | Expert mode only |
SAP_FEATURE_UI5 |
UI5/Fiori BSP | BSP app management |
SAP_FEATURE_TRANSPORT |
CTS transport | Superset of --enable-transports |
SAP_FEATURE_HANA |
HANA detection | AMDP, HANA SQL tools |
# Force all features off — fastest startup, smallest tool surface
-e SAP_FEATURE_ABAPGIT=off \
-e SAP_FEATURE_RAP=off \
-e SAP_FEATURE_AMDP=off \
-e SAP_FEATURE_UI5=off \
-e SAP_FEATURE_TRANSPORT=off \
-e SAP_FEATURE_HANA=off
# Force abapGit on without probing
-e SAP_FEATURE_ABAPGIT=on
Setting features to off reduces the number of registered MCP tools, which
keeps the AI's tool list shorter and lowers token usage.
Debugger¶
To share breakpoints with SAP GUI (cross-tool debugging), configure the same terminal ID that SAP GUI uses:
SAP GUI stores its terminal ID at:
- Windows: Registry HKCU\Software\SAP\ABAP Debugging\TerminalID
- Linux/Mac: ~/.SAP/ABAPDebugging/terminalId
Network / TLS¶
Self-signed or internal CA certificates¶
For SAP systems using self-signed certificates, either skip verification (development only) or add your CA to the image:
# Option 1: skip verification (NOT for production)
-e SAP_INSECURE=true
# Option 2: mount your CA certificate
docker run -i --rm \
-e SAP_URL=https://internal-sap:44300 \
-e SAP_USER=user -e SAP_PASSWORD=pass \
-v /etc/ssl/certs/company-ca.crt:/usr/local/share/ca-certificates/company-ca.crt:ro \
arc1
For a permanent fix, extend the image:
FROM ghcr.io/marianfoo/arc-1:latest
USER root
COPY company-ca.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates
USER arc1
HTTP/HTTPS proxy¶
arc1 respects standard Go proxy environment variables. Pass them via -e:
Connecting to a SAP system on the same Docker host¶
Use the host network or host.docker.internal (Docker Desktop):
# Linux — host network mode
docker run -i --rm --network host \
-e SAP_URL=http://localhost:50000 \
-e SAP_USER=user -e SAP_PASSWORD=pass \
arc1
# Docker Desktop (Mac/Windows)
-e SAP_URL=http://host.docker.internal:50000
Connecting to a SAP system in another Docker network¶
docker network create sap-net
docker run -i --rm \
--network sap-net \
-e SAP_URL=http://sap-container:50000 \
-e SAP_USER=user -e SAP_PASSWORD=pass \
arc1
MCP Client Integration¶
HTTP streamable (recommended)¶
Start the container once and point any MCP client at http://localhost:8080/mcp:
docker run -d --name arc1 \
-p 8080:8080 \
-e SAP_URL=https://my-sap-system:44300 \
-e SAP_USER=developer \
-e SAP_PASSWORD=secret \
ghcr.io/marianfoo/arc-1:latest
Then configure your MCP client to use the HTTP URL:
MCP clients that support the streamable HTTP transport (Claude Desktop ≥ 0.9, Cursor, etc.) can connect directly by URL — no
docker runsubprocess needed.
Claude Desktop (stdio fallback)¶
If your Claude Desktop version does not yet support HTTP MCP endpoints, use the stdio mode by overriding the transport:
{
"mcpServers": {
"arc1": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "SAP_URL=https://my-sap-system:44300",
"-e", "SAP_USER=developer",
"-e", "SAP_PASSWORD=secret",
"-e", "SAP_TRANSPORT=stdio",
"ghcr.io/marianfoo/arc-1:latest"
]
}
}
}
For a production system where you want read-only access:
{
"mcpServers": {
"arc1-prod": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "SAP_URL=https://prod-sap:44300",
"-e", "SAP_USER=readonly_user",
"-e", "SAP_PASSWORD=secret",
"-e", "SAP_TRANSPORT=stdio",
"-e", "SAP_READ_ONLY=true",
"ghcr.io/marianfoo/arc-1:latest"
]
}
}
}
Tip: Use
--env-fileinstead of individual-eflags to keep credentials out of the config file. Reference the absolute path to the env file:
Gemini CLI / Other Agents¶
Clients that support HTTP MCP can connect to http://localhost:8080/mcp directly
once the container is running. For stdio-only clients use docker run -i with
-e SAP_TRANSPORT=stdio. See docs/cli-agents/README.md for agent-specific
configuration examples.
Common Configurations¶
Read-Only for Production Review¶
Safe for handing to an AI agent with access to a production system:
docker run -d --rm \
-p 8080:8080 \
-e SAP_URL=https://prod:44300 \
-e SAP_USER=s_ai_viewer \
-e SAP_PASSWORD=secret \
-e SAP_READ_ONLY=true \
-e SAP_BLOCK_FREE_SQL=true \
-e SAP_FEATURE_TRANSPORT=off \
-e SAP_VERBOSE=true \
ghcr.io/marianfoo/arc-1:latest
Sandboxed AI (Z* packages only)¶
AI can read anything but only write to custom development packages:
docker run -i --rm \
-e SAP_URL=https://dev:44300 \
-e SAP_USER=developer \
-e SAP_PASSWORD=secret \
-e SAP_ALLOWED_PACKAGES="Z*,\$TMP" \
-e SAP_BLOCK_FREE_SQL=true \
-e SAP_DISALLOWED_OPS=D \
ghcr.io/marianfoo/arc-1:latest
This setup lets the AI read system objects, write only to custom packages, and prevents deletions and arbitrary SQL.
Cookie Authentication¶
docker run -i --rm \
-e SAP_URL=https://host:44300 \
-e SAP_COOKIE_FILE=/cookies/cookies.txt \
-v "${HOME}/.sap-cookies/my-system.txt:/cookies/cookies.txt:ro" \
ghcr.io/marianfoo/arc-1:latest
Corporate Proxy¶
docker run -i --rm \
--env-file .env \
-e HTTPS_PROXY=http://proxy.corp.example:3128 \
-e NO_PROXY=localhost,127.0.0.1 \
ghcr.io/marianfoo/arc-1:latest
Updating the Image¶
For a comprehensive update guide covering all deployment modes (Docker, BTP, npm), see setup-guide.md — Updating arc1.
Quick reference¶
# Pull a specific version (recommended for production)
docker pull ghcr.io/marianfoo/arc-1:0.2.0
# Pull latest (includes unreleased changes from main)
docker pull ghcr.io/marianfoo/arc-1:latest
# Stop, remove, restart with new image
docker stop arc1 && docker rm arc1
docker run -d --name arc1 -p 8080:8080 --env-file .env ghcr.io/marianfoo/arc-1:0.2.0
# Verify version
docker run --rm ghcr.io/marianfoo/arc-1:0.2.0 --version
Pinning a version (recommended)¶
For production or shared team environments, always pin to a specific version
tag rather than latest:
This ensures every team member uses the same binary regardless of when the image was pulled. Check the GitHub releases page for the latest version.
Rebuilding from source¶
Staying up to date automatically¶
Teams that want automatic image updates can use tools like
Renovate or
Dependabot to open PRs
when a new ghcr.io/marianfoo/arc-1 image tag is published.
Security Notes¶
-
Never bake credentials into images. Always pass
SAP_USER,SAP_PASSWORD,SAP_COOKIE_FILEat runtime via-eor--env-file. -
Protect your
.envfile. If using--env-file, ensure the file has restricted permissions (chmod 600) and is in.gitignore. -
Use
SAP_READ_ONLY=truefor production. Only enable write access on development systems. -
The container runs as a non-root user (
arc1:arc1) inside Alpine. There are no open ports — the attack surface is minimal. -
Cookie files contain session tokens. Mount them read-only (
:ro) and use short-lived sessions where possible. -
Use
SAP_INSECURE=false(the default). Only set it totruein isolated development environments with no sensitive data.
Troubleshooting¶
Container exits immediately¶
arc1 exits if stdin is closed. Make sure you are using -i:
docker run -i --rm ... # correct
docker run --rm ... # wrong — exits immediately
docker run -d --rm ... # wrong — detached mode breaks stdio
SAP URL is required error¶
The SAP_URL environment variable is mandatory. Verify it is being passed:
TLS certificate errors¶
Either add your CA certificate (see Network / TLS) or use
SAP_INSECURE=true in non-production environments.
authentication required error¶
Only one auth method is accepted. Check you are not accidentally setting both
SAP_USER/SAP_PASSWORD and SAP_COOKIE_FILE.
Enable verbose logging¶
Add -e SAP_VERBOSE=true to see startup decisions including which features were
detected and which safety rules are active. Logs go to stderr; they will not
interfere with MCP over stdout.
docker run -i --rm \
-e SAP_URL=https://host:44300 \
-e SAP_USER=user -e SAP_PASSWORD=pass \
-e SAP_VERBOSE=true \
arc1 2>arc1-debug.log
Tool not appearing in the AI client¶
- Check feature flags — features in
automode may have been turned off because the SAP component was not detected. Force them on with e.g.SAP_FEATURE_RAP=on. - Check
SAP_ALLOWED_OPS/SAP_DISALLOWED_OPS— operation filters can block tools at the handler level even if they are registered.