CLI for netcup SCP (Server Control Panel) REST API
Project description
Netcup Server Control Panel (SCP) CLI
Command-line interface for the Netcup Server Control Panel (SCP) REST API.
Manage servers, rDNS, snapshots, rescue systems, firewall policies, SSH keys, VLANs, and more from the terminal. Uses OAuth2 device-code flow; credentials are stored locally and refreshed automatically.
| Python | 3.10+ |
| License | MIT |
| API | SCP REST API · OpenAPI 2026.0703.095128 |
Why
netcupand notscp? The command is namednetcupto avoid clashing with the standard Unix scp (secure copy) command.
Table of contents
- Overview
- Requirements
- Installation
- Quick start
- Authentication
- Command reference
- Configuration
- Output and exit codes
- Troubleshooting
- API compatibility
- Project structure
- Getting help
- License
Overview
netcup CLI talks to the netcup SCP REST API. It supports:
- Servers — List, get, power on/off, set hostname/nickname; manage disks, interfaces, firewall, ISO, snapshots, rescue system, metrics, logs, image setup, guest agent, storage optimization.
- rDNS — Get, set, and delete reverse DNS for IPv4 and IPv6.
- Tasks — List (with filters), get, and cancel async tasks.
- Users — Current user info, get/update user; manage failover IPs, firewall policies, SSH keys, VLANs, user images/ISOs, logs.
- VLANs — Get VLAN by ID (standalone).
- Maintenance — Ping API and maintenance window info.
All commands that need a user context (e.g. users failoverips list) use the current user from your token by default; override with --user-id <id> when needed.
Requirements
- Python 3.10 or newer
- Network access to
https://www.servercontrolpanel.de - A netcup SCP account (device-code login in the browser)
Installation
From PyPI (when published)
pip install netcup-scp-cli
The PyPI distribution is netcup-scp-cli (the names netcup-cli and netcupctl are already taken). The installed command is still netcup; the Python import package is netcup_scp_cli.
From source (development or unreleased)
cd netcup-cli
pip install -e .
After installation, the netcup command is available.
Development (lint, format, test) — using uv (recommended):
uv sync --all-extras # create venv + install deps including dev
uv run ruff check src tests # lint
uv run ruff format src tests # format
uv run ruff check src tests --fix # auto-fix lint
uv run pytest # tests
Without uv: pip install -e ".[dev]" then run ruff and pytest as above.
netcup --help
Without installing (run as module)
cd netcup-cli
pip install -r requirements.txt
PYTHONPATH=src python -m netcup_scp_cli --help
Optional: use pipx (isolated environment)
pipx install netcup-scp-cli
# or from a local clone:
pipx install -e path/to/netcup-cli
Quick start
-
Log in (opens browser for device code):
netcup auth login
Complete the flow in the browser; the CLI stores a refresh token under
~/.config/netcup-scp-cli/. -
Check API:
netcup maintenance ping # OK
-
List servers:
netcup servers list
-
Inspect one server:
netcup servers get <server_id>
-
Power control:
netcup servers power <server_id> on netcup servers power <server_id> off --option POWEROFF
-
rDNS:
netcup rdns ipv4 get 1.2.3.4 netcup rdns ipv4 set 1.2.3.4 host.example.com
-
Current user:
netcup users info
For the full command set, see Command reference.
Authentication
- Method: OAuth2 device code flow (no password in the CLI).
- Stored credential: Only the refresh token is saved; access tokens are obtained on demand and not written to disk.
- Location:
~/.config/netcup-scp-cli/credentials(or$XDG_CONFIG_HOME/netcup-scp-cli/credentialsif set). File format:{"refresh_token": "…"}. - Lifetime: Refresh token remains valid as long as it is used at least once every 30 days (per SCP API).
Commands:
| Command | Description |
|---|---|
netcup auth login |
Start device-code login; optionally --no-save to avoid storing the token. |
netcup auth logout |
Remove the stored credentials file. |
netcup auth revoke |
Revoke the current refresh token on the server and delete the local file. |
netcup auth show |
Print the path of the credentials file and whether it exists. |
Security:
- The credentials file should be readable only by the current user (the CLI sets mode
0600when possible). - Do not share the credentials file or the refresh token.
- Use
netcup auth revokeif the token may have been exposed.
Command reference
Global options
--help,-h— Show help for the command or group.--version— Show netcup CLI version.
netcup auth
| Command | Description |
|---|---|
auth login [--no-save] |
Log in via device code; store refresh token unless --no-save. |
auth logout |
Remove stored credentials. |
auth revoke |
Revoke refresh token and remove credentials. |
auth show |
Show credentials file path and existence. |
netcup servers
| Command | Arguments / options | Description |
|---|---|---|
servers list |
[--limit N] [--offset N] [--ip IP] [--name NAME] [-q QUERY] [--sort FIELD] |
List servers (optional filters; --sort repeatable: name, nickname, prefix - for desc). |
servers get <server_id> |
[--no-live] |
Get one server; --no-live skips live info. |
servers power <server_id> on|off |
[--option POWERCYCLE|RESET|POWEROFF] |
Power on/off or power cycle. |
servers set-hostname <server_id> <hostname> |
Set server hostname. | |
servers set-nickname <server_id> <nickname> |
Set server nickname. |
Disks: servers disks <server_id>
| Subcommand | Arguments | Description |
|---|---|---|
list |
<server_id> |
List disks. |
get |
<server_id> <disk_name> |
Get one disk. |
supported-drivers |
<server_id> |
List supported storage drivers. |
set-driver |
<server_id> VIRTIO|VIRTIO_SCSI|IDE|SATA |
Set disk driver. |
format |
<server_id> <disk_name> |
Format disk (data loss). |
Interfaces: servers interfaces <server_id>
| Subcommand | Arguments / options | Description |
|---|---|---|
list |
<server_id> [--no-rdns] |
List interfaces. |
get |
<server_id> <mac> [--no-rdns] |
Get interface by MAC. |
create |
<server_id> --vlan-id ID --driver DRIVER |
Create interface (VLAN); driver: VIRTIO, E1000, E1000E, RTL8139, VMXNET3. |
update |
<server_id> <mac> --body JSON |
Update interface (e.g. driver). |
delete |
<server_id> <mac> |
Delete interface. |
Firewall (per interface): servers firewall <server_id> <mac>
| Subcommand | Description |
|---|---|
get [--consistency-check] |
Get firewall config. |
put --body JSON |
Set firewall (copiedPolicies, userPolicies, active). |
reapply |
Reapply firewall. |
restore-copied-policies |
Restore copied policies. |
ISO: servers iso <server_id>
| Subcommand | Options | Description |
|---|---|---|
get |
Get attached ISO. | |
attach |
--iso-id ID or --user-iso NAME; [--boot-cdrom] |
Attach ISO. |
detach |
Detach ISO. | |
images |
List available ISO images for server. |
Snapshots: servers snapshots <server_id>
| Subcommand | Arguments / options | Description |
|---|---|---|
list |
<server_id> |
List snapshots. |
get |
<server_id> <name> |
Get one snapshot. |
create |
<server_id> --name NAME [--description DESC] [--disk-name NAME] [--online] |
Create snapshot. |
delete |
<server_id> <name> |
Delete snapshot. |
export |
<server_id> <name> |
Export snapshot. |
revert |
<server_id> <name> |
Revert to snapshot. |
dryrun |
<server_id> [--disk-name NAME] [--online] |
Check if snapshot creation is possible. |
Rescue system: servers rescue <server_id>
| Subcommand | Description |
|---|---|
get |
Get rescue system status. |
activate |
Activate rescue system. |
deactivate |
Deactivate rescue system. |
Metrics: servers metrics <server_id>
| Subcommand | Options | Description |
|---|---|---|
cpu |
[--hours N] |
CPU metrics. |
disk |
[--hours N] |
Disk metrics. |
network |
[--hours N] |
Network metrics. |
packet |
[--hours N] |
Network packet metrics. |
Other server commands
| Command | Arguments / options | Description |
|---|---|---|
servers logs list |
<server_id> [--limit N] [--offset N] |
Server logs. |
servers guest-agent get |
<server_id> |
Guest agent data. |
servers image flavours |
<server_id> |
Image flavours for setup. |
servers image setup |
<server_id> --body JSON |
Setup image (JSON body per API). |
servers user-image setup |
<server_id> --name NAME [--disk-name NAME] [--email-notification] |
Setup user image. |
servers storage-optimization start |
<server_id> [--disks NAME...] [--start-after] |
Start storage optimization. |
netcup rdns
| Command | Example |
|---|---|
rdns ipv4 get <ip> |
netcup rdns ipv4 get 1.2.3.4 |
rdns ipv4 set <ip> <rdns> |
netcup rdns ipv4 set 1.2.3.4 host.example.com |
rdns ipv4 delete <ip> |
|
rdns ipv6 get <ip> |
|
rdns ipv6 set <ip> <rdns> |
|
rdns ipv6 delete <ip> |
netcup tasks
| Command | Options | Description |
|---|---|---|
tasks list |
[--limit N] [--offset N] [-q QUERY] [--server-id ID] [--state STATE] |
List tasks. |
tasks get <uuid> |
Get one task. | |
tasks cancel <uuid> |
Cancel task. |
netcup users
| Command | Options | Description |
|---|---|---|
users info |
Current user (from token). | |
users get <user_id> |
Get user by ID. | |
users update <user_id> --body JSON |
Update user (UserSave schema). |
All commands below support --user-id <id>; if omitted, the current user is used.
Failover IPs: users failoverips v4|v6
| Subcommand | Example |
|---|---|
v4 list |
[--ip IP] [--server-id ID] |
v4 route <id> <server_id> |
Route failover IPv4 to server. |
v6 list |
Same options as v4. |
v6 route <id> <server_id> |
Route failover IPv6 to server. |
Firewall policies: users firewall-policies
| Subcommand | Options |
|---|---|
list |
[--limit N] [--offset N] [-q QUERY] |
get <policy_id> |
[--with-servers-count] |
create --body JSON |
JSON: name, optional description, rules. |
update <policy_id> --body JSON |
|
delete <policy_id> |
SSH keys: users ssh-keys
| Subcommand | Options |
|---|---|
list |
|
add --name NAME --key KEY |
Add public key. |
delete <key_id> |
VLANs: users vlans
| Subcommand | Options |
|---|---|
list |
[--server-id ID] |
get <vlan_id> |
|
update <vlan_id> <name> |
User images (S3): users images
| Subcommand | Description |
|---|---|
list |
List user images. |
delete <key> |
Delete image by key. |
download-url <key> |
Get presigned download URL. |
upload <key> <path> |
Upload local file (--multipart / --no-multipart, --part-size). |
User ISOs (S3): users isos
| Subcommand | Description |
|---|---|
list |
List user ISOs. |
delete <key> |
Delete ISO by key. |
download-url <key> |
Get presigned download URL. |
upload <key> <path> |
Upload local file (--multipart / --no-multipart, --part-size). |
User logs: users logs list [--limit N] [--offset N]
netcup vlans
| Command | Description |
|---|---|
vlans get <vlan_id> |
Get VLAN by ID (no user scope). |
netcup maintenance
| Command | Description |
|---|---|
maintenance ping |
Check API availability (returns OK). |
maintenance info |
Maintenance window information. |
Configuration
Credentials
- Path:
~/.config/netcup-scp-cli/credentials(Linux/macOS). On Windows,%APPDATA%\netcup-scp-cli\credentialsis not used by default; the CLI uses$XDG_CONFIG_HOMEif set, otherwise~/.config. - Override directory: Set
XDG_CONFIG_HOME(e.g.export XDG_CONFIG_HOME=$HOME/.config). - Format: JSON with a single key:
{"refresh_token": "…"}. Do not edit manually unless you know the token value.
API endpoints (hardcoded)
The CLI uses these base URLs (not configurable via env or file):
- Auth:
https://www.servercontrolpanel.de/realms/scp/protocol/openid-connect - API root:
https://www.servercontrolpanel.de/scp-core/api - API v1:
https://www.servercontrolpanel.de/scp-core/api/v1
To point at another environment, you would need to change the code in netcup_scp_cli/config.py.
Output and exit codes
- Success: Exit code
0. List/get commands print JSON to stdout (pretty-printed). - Failure: Exit code
1. Error message is printed to stderr (and often in red when run in a TTY). - JSON: All API responses that return JSON are printed as-is (indented). Use
jqfor further processing if needed, e.g.netcup servers list | jq '.[].name'.
Troubleshooting
API error: 406
- Cause: The server could not satisfy the
Acceptheader (e.g. endpoint returnstext/plainbut client asked forapplication/json). - Relevant command:
maintenance ping— fixed in current version by requestingAccept: text/plainfor that call. Ensure you use the latest code.
No stored credentials. Run: netcup auth login
- Cause: No refresh token found in the credentials file.
- Fix: Run
netcup auth loginand complete the browser flow. Ensure the credentials file exists at the path shown bynetcup auth show.
Token refresh failed: 401 or 403
- Cause: Refresh token expired or was revoked (e.g. after 30 days of no use, or revoked in SCP account).
- Fix: Run
netcup auth loginagain. If needed, revoke old access in SCP: Account → Applications → scp → Remove access, then log in again.
Access denied on device authorization
- Cause: You declined the authorization in the browser or closed the page before approving.
- Fix: Run
netcup auth loginagain and approve the application in the browser.
Device code expired
- Cause: You did not complete the browser step within the validity window (e.g. 600 seconds).
- Fix: Run
netcup auth loginagain and complete the flow promptly.
API error: 404 / 422 / 503
- Cause: Resource not found, validation error, or service temporarily unavailable (e.g. maintenance).
- Action: Check IDs and request body; for 503, retry later or check maintenance info:
netcup maintenance info.
API compatibility
- Spec: The CLI is built against the SCP REST API as described in the OpenAPI spec (version 2026.0703.095128 in the bundled
openapi.json). - Coverage: Most endpoints from the spec are implemented (servers, rDNS, tasks, users, failover IPs, firewall policies, SSH keys, VLANs, images/ISOs including S3 upload, logs, maintenance). Intentionally skipped:
GET /openapi(use bundled snapshot) andPOST /openapi/mcp(explore-only; no CLI wrapper). Coverage is guarded bytests/test_openapi_coverage.py. - Breaking changes: If the API introduces breaking changes, the CLI may need updates. Check the netcup SCP API forum and release notes.
OpenAPI spec file
The file openapi.json in the project root is a snapshot of the spec fetched from:
curl -s -X GET 'https://www.servercontrolpanel.de/scp-core/api/v1/openapi' -H 'accept: application/json' -o openapi.json
You can refresh it to align with the latest API. The CLI does not read this file at runtime; it is for reference and for the OpenAPI coverage tests.
SCP OpenAPI MCP endpoint (Miscellaneous)
The bundled spec documents POST /v1/openapi/mcp (tag Miscellaneous; some explorers label it like #/Miscellaneous/post_api_v1_openapi_mcp). Summary: SCP OpenAPI Spec MCP Server.
Purpose (from the spec): SCP (Server Control Panel) API Explorer — OpenAPI specification explorer for the netcup SCP REST API. It provides documentation, schema definitions, and guidance for building SCP API clients and scripts. It does not execute SCP API calls directly.
Full URL: https://www.servercontrolpanel.de/scp-core/api/v1/openapi/mcp
Use this with an MCP-capable client if you want explore-only access to the API surface. It uses the same https://www.servercontrolpanel.de/scp-core/api/v1 base as other authenticated routes; this CLI does not ship a wrapper for the MCP protocol (JSON-RPC message exchange over HTTP).
Project structure
netcup-cli/ # git repo (local clone)
├── pyproject.toml # Package metadata (PyPI name: netcup-scp-cli)
├── requirements.txt # requests, click
├── README.md # This file
├── openapi.json # Snapshot of SCP OpenAPI spec
├── .github/workflows/ci.yml
├── tests/ # OpenAPI coverage + unit tests
└── src/netcup_scp_cli/
├── __init__.py # Version
├── config.py # URLs, credential path
├── auth.py # Device code, refresh, revoke, load/save credentials
├── client.py # HTTP client (Bearer token, Accept header)
├── exceptions.py # SCPError, AuthError, APIError, ConfigError
├── output.py # JSON formatting
├── api/ # API layer (one module per resource)
│ ├── base.py
│ ├── s3_upload.py # Shared S3 multipart / single-shot upload helper
│ ├── maintenance.py
│ ├── rdns.py
│ ├── servers.py
│ ├── servers_disks.py
│ ├── servers_interfaces.py
│ ├── servers_iso.py
│ ├── servers_snapshots.py
│ ├── servers_rescue.py
│ ├── servers_metrics.py
│ ├── servers_logs.py
│ ├── servers_image.py
│ ├── servers_guest.py
│ ├── servers_gpu.py
│ ├── servers_storage.py
│ ├── tasks.py
│ ├── users.py
│ ├── user_failoverips.py
│ ├── user_firewall_policies.py
│ ├── user_ssh_keys.py
│ ├── user_vlans.py
│ ├── user_images.py
│ ├── user_isos.py
│ └── user_logs.py
└── cli/ # Click commands
├── main.py # Entry point, group registration
├── helpers.py # user_id resolution
├── auth_cmd.py
├── servers_cmd.py
├── servers_disks_cmd.py
├── servers_interfaces_cmd.py
├── servers_iso_cmd.py
├── servers_snapshots_cmd.py
├── servers_rescue_cmd.py
├── servers_metrics_cmd.py
├── servers_misc_cmd.py
├── rdns_cmd.py
├── tasks_cmd.py
├── users_cmd.py
├── users_resources_cmd.py
├── vlans_cmd.py
└── maintenance_cmd.py
Getting help
- General:
netcup --help - Command group:
netcup servers --help,netcup users --help, etc. - Specific command:
netcup servers power --help,netcup rdns ipv4 set --help - Version:
netcup --version
License
MIT. See the LICENSE file in the project repository for the full license text.
Links
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file netcup_scp_cli-1.0.0.tar.gz.
File metadata
- Download URL: netcup_scp_cli-1.0.0.tar.gz
- Upload date:
- Size: 37.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd60b653a0231741cb125012f8ca5f41706c9943d8ebaeb04d733672ddc8fd48
|
|
| MD5 |
3affa066085a9e0f0df849bd52062704
|
|
| BLAKE2b-256 |
ae80605fbdc79994429c3d30df6f5c02307d25b9859d47b8aa3581f4f826c14a
|
Provenance
The following attestation bundles were made for netcup_scp_cli-1.0.0.tar.gz:
Publisher:
publish.yml on pavelpikta/netcup-scp-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
netcup_scp_cli-1.0.0.tar.gz -
Subject digest:
cd60b653a0231741cb125012f8ca5f41706c9943d8ebaeb04d733672ddc8fd48 - Sigstore transparency entry: 2190137773
- Sigstore integration time:
-
Permalink:
pavelpikta/netcup-scp-cli@ceb9770a77c8ba43a7696b0e9f782131f59b7f5b -
Branch / Tag:
refs/tags/1.0.0 - Owner: https://github.com/pavelpikta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ceb9770a77c8ba43a7696b0e9f782131f59b7f5b -
Trigger Event:
release
-
Statement type:
File details
Details for the file netcup_scp_cli-1.0.0-py3-none-any.whl.
File metadata
- Download URL: netcup_scp_cli-1.0.0-py3-none-any.whl
- Upload date:
- Size: 46.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e931e0b58ed50c8160832f585cbf3d70a07ba072145a411b7ece468114b681b9
|
|
| MD5 |
dc634c6df853e6b3b8c00a1158cf41ab
|
|
| BLAKE2b-256 |
80c98207cd9ffcdfba54424eac437271f1d2cd2785cef06ed3877eec27a29ed2
|
Provenance
The following attestation bundles were made for netcup_scp_cli-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on pavelpikta/netcup-scp-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
netcup_scp_cli-1.0.0-py3-none-any.whl -
Subject digest:
e931e0b58ed50c8160832f585cbf3d70a07ba072145a411b7ece468114b681b9 - Sigstore transparency entry: 2190137782
- Sigstore integration time:
-
Permalink:
pavelpikta/netcup-scp-cli@ceb9770a77c8ba43a7696b0e9f782131f59b7f5b -
Branch / Tag:
refs/tags/1.0.0 - Owner: https://github.com/pavelpikta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ceb9770a77c8ba43a7696b0e9f782131f59b7f5b -
Trigger Event:
release
-
Statement type: