Skip to content

fullya99/netcup-cli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🚀 netcup-api

A modern, spec-driven Python client & CLI for the netcup Server Control Panel (SCP) REST API

CI Python License: MIT Built on OpenAPI

Manage your netcup VPS fleet from the terminal — power, snapshots, firewall, rDNS, metrics — with every API endpoint one command away.

Install · Authenticate · Commands · Any endpoint · Library


Note

netcup's legacy SOAP webservice was shut down on 2026‑05‑01. This tool targets the modern REST API (scp-core) with OAuth2 / Keycloak device‑flow auth.

✨ Why this one

🧭 Spec-driven Every command is backed by the API's own OpenAPI spec — no hand-maintained endpoint lists.
🔌 All endpoints, always Curated commands for daily ops + a generic layer (call / api) that reaches all operations.
🔮 Future-proof New netcup endpoint? netcup spec update and it's instantly usable — zero code changes.
🔐 Secure by default Only the offline refresh token is stored (mode 0600); the 300 s access token is cached & auto-refreshed.
🎨 Pretty output Rich tables with humanized sizes, or --json for clean jq piping.
🤝 Shared core Same engine powers the netcup-mcp server.

📦 Install

pip install netcup-api          # from PyPI (once published)
# or from source:
git clone https://github.com/fullya99/netcup-cli && cd netcup-cli
pip install -e .

🔑 Authenticate (once)

Authentication uses the OAuth2 device-code flow against netcup's Keycloak realm.

netcup auth login          # prints a URL + code, opens your browser
netcup auth status         # who am I? (incl. SCP userId)
netcup auth logout         # revoke the refresh token & wipe local creds

Tip

The offline refresh token stays valid as long as it's used at least once every 30 days. Only the refresh token is persisted (~/.config/netcup-api/, 0600); the access token is cached and refreshed automatically.

⚡ Everyday commands

# Servers
netcup servers list                         # table of your servers
netcup servers get <serverId>
netcup servers start <serverId>             # power ON
netcup servers stop  <serverId>             # power OFF
netcup servers set-hostname <serverId> web01.example.com

# Snapshots
netcup snapshots create <serverId> before-upgrade --online
netcup snapshots revert <serverId> before-upgrade
netcup snapshots list   <serverId>

# Networking
netcup rdns set 203.0.113.10 host.example.com
netcup rdns set 2a03:4000:: host.example.com --v6
netcup interfaces <serverId>

# Keys, firewall, disks, ISO/images, metrics, tasks
netcup ssh-keys add laptop --key-file ~/.ssh/id_ed25519.pub
netcup firewall policies
netcup disks <serverId>
netcup iso list   /   netcup iso available <serverId>
netcup images list
netcup metrics <serverId> cpu               # cpu | disk | network | network/packet
netcup tasks list

Add --json / -j to any command for raw JSON (great with jq).

🌐 Reach any endpoint

The CLI is OpenAPI-driven, so the full API is one command away — even endpoints with no curated wrapper:

netcup endpoints                 # list every endpoint
netcup endpoints snapshot        # filter by text
netcup endpoints --tag "Server Networking"
netcup describe get-servers-by-serverId       # params + request body schema

# Invoke any operation by id (userId is auto-filled). Path via -p, query via -q, body via -d.
netcup call get-servers -q limit=10
netcup call patch-servers-by-serverId -p serverId=12345 -d '{"state":"ON"}'

# Or the raw escape hatch:
netcup api GET   /api/v1/servers -q q=web
netcup api PATCH /api/v1/servers/12345 -d '{"hostname":"web01"}'

Keep the spec current:

netcup spec info       # bundled spec version + endpoint/tag count
netcup spec update     # pull the latest spec from the live API

🐍 Use as a library

from netcup_api import NetcupClient

c = NetcupClient()                       # uses your stored credentials
for s in c.list_servers(limit=10):
    print(s["name"], s["state"])

c.set_server_state("12345", "ON")
c.call("post-servers-by-serverId-snapshots",
       path_params={"serverId": "12345"},
       body={"name": "snap1", "onlineSnapshot": True})

⚠️ Good to know

Topic Detail
Access token valid 300 s — refreshed automatically by the client
Refresh token offline; dies after 30 days unused → re-run netcup auth login
PATCH /servers/{id} one attribute per request (else HTTP 400); Content-Type: application/merge-patch+json (handled for you)
Image/ISO uploads the API returns presigned S3 URLs; the binary PUT to that URL is a separate step

🔧 Configuration

Env var Default
NETCUP_CONFIG_DIR ~/.config/netcup-api
NETCUP_BASE_HOST https://www.servercontrolpanel.de
NETCUP_OPENAPI path to an explicit spec file (overrides bundled/cached)

🛠️ Development

pip install -e ".[dev]"
ruff check .          # lint
pytest -q             # offline tests (no network, no auth)

See CLAUDE.md for architecture, the spec-driven principle, and contribution rules.

🤖 AI assistant integration

Want Claude / any MCP client to drive your infra in natural language? Pair this with the companion MCP server: netcup-mcp.

📄 License

MIT © Antoine Chenais

Not affiliated with netcup GmbH. Use at your own risk on production infrastructure.

About

Modern, spec-driven Python client & CLI for the netcup Server Control Panel (SCP) REST API

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages