Skip to content

Commit 0d72248

Browse files
ArthurC-Ninaclaude
andcommitted
docs: modern README, MIT LICENSE, correct project URLs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 21a3c98 commit 0d72248

3 files changed

Lines changed: 117 additions & 66 deletions

File tree

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Antoine Chenais
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 94 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,167 @@
1-
# netcup-api · `netcup` CLI
1+
<div align="center">
22

3-
A solid, spec-driven Python client **library** and **command-line interface** for the
4-
[netcup Server Control Panel (SCP) REST API](https://www.netcup.com/en/helpcenter/documentation/server/rest-api).
3+
# 🚀 netcup-api
54

6-
> The legacy SOAP webservice was shut down on **2026-05-01**. This tool targets the
7-
> modern REST API (`scp-core`, OAuth2 / Keycloak device flow).
5+
### A modern, spec-driven Python client & CLI for the netcup Server Control Panel (SCP) REST API
86

9-
## Why this design
7+
[![CI](https://github.com/fullya99/netcup-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/fullya99/netcup-cli/actions/workflows/ci.yml)
8+
[![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
9+
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](#-license)
10+
[![Built on OpenAPI](https://img.shields.io/badge/driven%20by-OpenAPI-6BA539.svg?logo=openapiinitiative&logoColor=white)](https://www.servercontrolpanel.de/scp-core/api/v1/openapi)
1011

11-
Everything is driven by the API's own **OpenAPI specification**, which is bundled and
12-
can be refreshed at any time (`netcup spec update`). This means:
12+
*Manage your netcup VPS fleet from the terminal — power, snapshots, firewall, rDNS, metrics — with **every** API endpoint one command away.*
1313

14-
- **All endpoints are covered** — not just the hand-written convenience commands.
15-
`netcup endpoints` lists them; `netcup call <operationId>` invokes any of them.
16-
- **Future-proof** — when netcup adds or changes endpoints, refresh the spec and the
17-
new operations are immediately available via `endpoints` / `call`, with no code change.
18-
- **No hard-coded hostnames or guessing** — the auth flow and base URLs are explicit and
19-
documented in `netcup_api/config.py`.
14+
[Install](#-install) · [Authenticate](#-authenticate-once) · [Commands](#-everyday-commands) · [Any endpoint](#-reach-any-endpoint) · [Library](#-use-as-a-library)
2015

21-
## Install
16+
</div>
17+
18+
---
19+
20+
> [!NOTE]
21+
> netcup's legacy SOAP webservice was shut down on **2026‑05‑01**. This tool targets the modern
22+
> **REST API** (`scp-core`) with OAuth2 / Keycloak device‑flow auth.
23+
24+
## ✨ Why this one
25+
26+
| | |
27+
|---|---|
28+
| 🧭 **Spec-driven** | Every command is backed by the API's own OpenAPI spec — no hand-maintained endpoint lists. |
29+
| 🔌 **All endpoints, always** | Curated commands for daily ops **+** a generic layer (`call` / `api`) that reaches *all* operations. |
30+
| 🔮 **Future-proof** | New netcup endpoint? `netcup spec update` and it's instantly usable — zero code changes. |
31+
| 🔐 **Secure by default** | Only the offline refresh token is stored (mode `0600`); the 300 s access token is cached & auto-refreshed. |
32+
| 🎨 **Pretty output** | Rich tables with humanized sizes, or `--json` for clean `jq` piping. |
33+
| 🤝 **Shared core** | Same engine powers the [`netcup-mcp`](https://github.com/fullya99/netcup-mcp) server. |
34+
35+
## 📦 Install
2236

2337
```bash
2438
pip install netcup-api # from PyPI (once published)
25-
# or, from source:
39+
# or from source:
40+
git clone https://github.com/fullya99/netcup-cli && cd netcup-cli
2641
pip install -e .
2742
```
2843

29-
## Authenticate (once)
44+
## 🔑 Authenticate (once)
3045

3146
Authentication uses the OAuth2 **device-code flow** against netcup's Keycloak realm.
32-
Only the long-lived *offline refresh token* is stored (`~/.config/netcup-api/credentials.json`, mode 0600);
33-
the 300-second access token is cached separately and refreshed automatically.
3447

3548
```bash
36-
netcup auth login # prints a URL + user code, opens your browser
37-
netcup auth status # show who you are (incl. SCP userId)
38-
netcup auth logout # revoke the refresh token and wipe local creds
49+
netcup auth login # prints a URL + code, opens your browser
50+
netcup auth status # who am I? (incl. SCP userId)
51+
netcup auth logout # revoke the refresh token & wipe local creds
3952
```
4053

41-
> The offline refresh token stays valid **as long as it is used at least once every 30 days**.
42-
> If unused longer, re-run `netcup auth login`.
54+
> [!TIP]
55+
> The offline refresh token stays valid **as long as it's used at least once every 30 days**.
56+
> Only the refresh token is persisted (`~/.config/netcup-api/`, `0600`); the access token is cached and refreshed automatically.
4357
44-
## Daily-driver commands
58+
## ⚡ Everyday commands
4559

4660
```bash
61+
# Servers
4762
netcup servers list # table of your servers
4863
netcup servers get <serverId>
4964
netcup servers start <serverId> # power ON
5065
netcup servers stop <serverId> # power OFF
51-
netcup servers set-hostname <serverId> host.example.com
66+
netcup servers set-hostname <serverId> web01.example.com
5267

53-
netcup snapshots list <serverId>
68+
# Snapshots
5469
netcup snapshots create <serverId> before-upgrade --online
5570
netcup snapshots revert <serverId> before-upgrade
56-
netcup snapshots delete <serverId> before-upgrade
71+
netcup snapshots list <serverId>
5772

73+
# Networking
5874
netcup rdns set 203.0.113.10 host.example.com
59-
netcup rdns set 2a03:... host.example.com --v6
75+
netcup rdns set 2a03:4000:: host.example.com --v6
76+
netcup interfaces <serverId>
6077

61-
netcup ssh-keys list
78+
# Keys, firewall, disks, ISO/images, metrics, tasks
6279
netcup ssh-keys add laptop --key-file ~/.ssh/id_ed25519.pub
63-
6480
netcup firewall policies
81+
netcup disks <serverId>
82+
netcup iso list / netcup iso available <serverId>
83+
netcup images list
84+
netcup metrics <serverId> cpu # cpu | disk | network | network/packet
6585
netcup tasks list
66-
netcup metrics <serverId> cpu
67-
netcup interfaces <serverId>
68-
69-
netcup disks <serverId> # disk table (sizes humanized)
70-
netcup images list # your uploaded custom images
71-
netcup images flavours <serverId> # available install flavours
72-
netcup iso list # your uploaded ISOs
73-
netcup iso attached <serverId> # ISO currently attached
74-
netcup iso available <serverId> # ISO images available to attach
7586
```
7687

77-
Add `--json` / `-j` to any command for raw JSON output (great for scripting with `jq`).
88+
Add `--json` / `-j` to **any** command for raw JSON (great with `jq`).
89+
90+
## 🌐 Reach *any* endpoint
7891

79-
## Reach *any* endpoint (spec-driven)
92+
The CLI is OpenAPI-driven, so the full API is one command away — even endpoints with no curated wrapper:
8093

8194
```bash
82-
netcup endpoints # list all endpoints
83-
netcup endpoints snapshot # filter
95+
netcup endpoints # list every endpoint
96+
netcup endpoints snapshot # filter by text
8497
netcup endpoints --tag "Server Networking"
85-
netcup describe get-servers-by-serverId # params + request schema
98+
netcup describe get-servers-by-serverId # params + request body schema
8699

87-
# Invoke any operation by id; path params via -p, query via -q, body via -d/--data-file/--stdin.
88-
# (userId is auto-filled from your identity.)
100+
# Invoke any operation by id (userId is auto-filled). Path via -p, query via -q, body via -d.
89101
netcup call get-servers -q limit=10
90102
netcup call patch-servers-by-serverId -p serverId=12345 -d '{"state":"ON"}'
91-
netcup call post-rdns-ipv4 -d '{"ip":"203.0.113.10","rdns":"host.example.com"}'
92103

93104
# Or the raw escape hatch:
94-
netcup api GET /api/v1/servers -q q=web
105+
netcup api GET /api/v1/servers -q q=web
95106
netcup api PATCH /api/v1/servers/12345 -d '{"hostname":"web01"}'
96107
```
97108

98-
## Keep the spec current
109+
Keep the spec current:
99110

100111
```bash
101112
netcup spec info # bundled spec version + endpoint/tag count
102-
netcup spec update # download the latest spec from the live API
113+
netcup spec update # pull the latest spec from the live API
103114
```
104115

105-
## Use as a library
116+
## 🐍 Use as a library
106117

107118
```python
108119
from netcup_api import NetcupClient
109120

110-
c = NetcupClient() # uses your stored credentials
111-
servers = c.list_servers(limit=10)
121+
c = NetcupClient() # uses your stored credentials
122+
for s in c.list_servers(limit=10):
123+
print(s["name"], s["state"])
124+
112125
c.set_server_state("12345", "ON")
113126
c.call("post-servers-by-serverId-snapshots",
114127
path_params={"serverId": "12345"},
115128
body={"name": "snap1", "onlineSnapshot": True})
116129
```
117130

118-
## Known limits & gotchas (from the API)
131+
## ⚠️ Good to know
119132

120133
| Topic | Detail |
121134
|------|--------|
122-
| Access token | valid **300 s**; auto-refreshed by this client |
123-
| Refresh token | offline; dies after **30 days unused** → re-login |
124-
| `PATCH /servers/{id}` | **one attribute per request** (else HTTP 400); `Content-Type: application/merge-patch+json` (handled automatically) |
125-
| Image/ISO uploads | the API returns presigned S3 URLs; the binary upload itself is a separate `PUT` to that URL (not yet wrapped — use the returned URL directly) |
135+
| Access token | valid **300 s**refreshed automatically by the client |
136+
| Refresh token | offline; dies after **30 days unused** → re-run `netcup auth login` |
137+
| `PATCH /servers/{id}` | **one attribute per request** (else HTTP 400); `Content-Type: application/merge-patch+json` (handled for you) |
138+
| Image/ISO uploads | the API returns presigned S3 URLs; the binary `PUT` to that URL is a separate step |
126139

127-
## Configuration (env vars)
140+
## 🔧 Configuration
128141

129-
| Var | Default |
130-
|-----|---------|
142+
| Env var | Default |
143+
|---------|---------|
131144
| `NETCUP_CONFIG_DIR` | `~/.config/netcup-api` |
132145
| `NETCUP_BASE_HOST` | `https://www.servercontrolpanel.de` |
133146
| `NETCUP_OPENAPI` | path to an explicit spec file (overrides bundled/cached) |
134147

135-
## License
148+
## 🛠️ Development
149+
150+
```bash
151+
pip install -e ".[dev]"
152+
ruff check . # lint
153+
pytest -q # offline tests (no network, no auth)
154+
```
155+
156+
See [`CLAUDE.md`](./CLAUDE.md) for architecture, the spec-driven principle, and contribution rules.
157+
158+
## 🤖 AI assistant integration
159+
160+
Want Claude / any MCP client to drive your infra in natural language? Pair this with the
161+
companion MCP server: **[netcup-mcp](https://github.com/fullya99/netcup-mcp)**.
162+
163+
## 📄 License
164+
165+
[MIT](https://opensource.org/licenses/MIT) © Antoine Chenais
136166

137-
MIT
167+
<div align="center"><sub>Not affiliated with netcup GmbH. Use at your own risk on production infrastructure.</sub></div>

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ dependencies = [
2424
]
2525

2626
[project.urls]
27-
Homepage = "https://github.com/achenais/netcup-cli"
28-
Issues = "https://github.com/achenais/netcup-cli/issues"
27+
Homepage = "https://github.com/fullya99/netcup-cli"
28+
Issues = "https://github.com/fullya99/netcup-cli/issues"
2929

3030
[project.scripts]
3131
netcup = "netcup_api.cli:main"

0 commit comments

Comments
 (0)