Metadata-Version: 2.5
Name: sillydev
Version: 0.1.0
Summary: Python client, CLI and MCP server for the Silly Development panel API.
Project-URL: Homepage, https://sillydev.co.uk
Project-URL: Documentation, https://sillydev.co.uk/docs/panel
Project-URL: Source, https://github.com/TheGamer3514/silly-development/tree/main/sdk/python
Author: Silly Development
License-Expression: MIT
License-File: LICENSE
Keywords: api,bot hosting,mcp,sdk,silly development,sillydev
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx<1,>=0.23
Provides-Extra: dev
Requires-Dist: mcp>=1.2; (python_version >= '3.10') and extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp>=1.2; (python_version >= '3.10') and extra == 'mcp'
Description-Content-Type: text/markdown

# sillydev

The Python client for the [Silly Development](https://sillydev.co.uk) panel API: start, stop and
restart your bots, send console commands, manage files and backups, and deploy from Git, all from
your own code. It also comes with a `sillydev` command line tool and an MCP server for AI assistants.

Full API docs: <https://sillydev.co.uk/docs/panel>

## Install

```sh
pip install sillydev
```

Python 3.9 or later. The only dependency is [httpx](https://www.python-httpx.org/).

## Quick start

Create an API key in the panel under **Account, API keys** and put it in the `SILLYDEV_API_KEY`
environment variable. Keys start with `sdevc_` (older `ptlc_` keys keep working).

```python
from sillydev import SillyDev

client = SillyDev()  # or SillyDev(api_key="sdevc_...")

for server in client.panel.servers.list():
    print(server["identifier"], server["name"])

client.panel.servers.power("1a7ce997", "restart")
client.panel.servers.command("1a7ce997", "say Back in a minute")
```

Each server has an 8-character identifier, shown in the panel and in the `identifier` field of
every server. Every method takes it (or the full UUID) as its first argument.

## Examples

```python
from sillydev import SillyDev

client = SillyDev()
panel = client.panel
server = "1a7ce997"

# Live state and resource use
stats = panel.servers.resources(server)
print(stats["current_state"], stats["resources"]["memory_bytes"])

# Files
for f in panel.files.list(server, directory="/"):
    print(f["name"], f["size"])
config = panel.files.read(server, "/config.json")
panel.files.write(server, "/config.json", config.replace("debug", "info"))

# Backups
backup = panel.backups.create(server, name="before-update")
print(backup["uuid"])

# Deploy the linked Git repository
deployment = panel.git.deploy(server)
print(deployment["status"])  # "queued"

# Startup variables
panel.startup.set_variable(server, "MAIN_FILE", "bot.py")
```

### Responses

Methods return plain dicts and lists. The API's `{"object": ..., "attributes": {...}}` envelopes
are removed, so `panel.servers.get(server)["name"]` just works.

### Pagination

`panel.servers.list()`, `panel.backups.list()`, `panel.servers.activity()` and
`panel.account.activity()` return iterators that fetch the next page as you go. For one page at a
time, with the list's `meta`, use the `*_page` methods:

```python
page = panel.servers.list_page(page=1, per_page=25)
print(page.total, page.has_next, page.meta.get("pending_orders"))
```

### Async

```python
import asyncio
from sillydev import AsyncSillyDev

async def main():
    async with AsyncSillyDev() as client:
        async for server in client.panel.servers.list():
            print(server["name"])
        await client.panel.servers.restart("1a7ce997")

asyncio.run(main())
```

### Errors

Every error is a `sillydev.SillyDevError`. Errors the API answers with have their own class:

| Status | Exception |
| --- | --- |
| 400 | `BadRequestError` |
| 401 | `AuthenticationError` |
| 403 | `PermissionDeniedError` |
| 404 | `NotFoundError` |
| 409 | `ConflictError` |
| 422 | `ValidationError` (`.errors` has every problem, `.fields` groups them by field) |
| 429 | `RateLimitError` (`.retry_after` in seconds) |
| 5xx | `ServerError` |

If the API cannot be reached at all you get `APIConnectionError`.

```python
from sillydev import ConflictError, NotFoundError

try:
    panel.servers.restart(server)
except ConflictError:
    print("The server is installing or being transferred; try again later")
except NotFoundError:
    print("No such server, or the key cannot reach it")
```

### Retries and limits

Each account may make 768 requests a minute. Requests answered 429 are retried
twice by default, and reads answered 502, 503 or 504 too (a command or power action is not
repeated, since the server may already have run it), waiting as long as the `Retry-After` header asks. Change it with
`SillyDev(max_retries=...)`; `0` turns retries off.

Ordering a paid server (`panel.billing.checkout(...)`) always sends an `Idempotency-Key`, so a
retried request never buys twice. Pass `idempotency_key=` yourself to make retries safe across runs.

### Everything else

`client.panel` has one attribute for each area of the API:

| Attribute | What it covers |
| --- | --- |
| `servers` | list, get, delete, resources, activity, permissions, power, command, share logs, websocket |
| `settings` | rename, reinstall, Docker image, status badge |
| `startup` | startup command and variables |
| `files` | list, read, write, rename, copy, compress, decompress, delete, folders, chmod, pull, signed URLs |
| `backups` | list, create, get, delete, download, lock, restore |
| `schedules` | schedules and their tasks |
| `network` | allocations (ports) |
| `subusers` | other accounts with access to a server |
| `git` | link a repository, deploy, deployments, `sillydev.conf` |
| `account` | the key's account and its activity |
| `server_creation` | create free servers |
| `databases` | free MariaDB and Postgres databases |
| `billing` | paid servers and subscriptions |

Every method's docstring names the API operation it calls, so you can look it up in the
[API reference](https://sillydev.co.uk/docs/panel).

## Command line

```sh
export SILLYDEV_API_KEY="sdevc_..."

sillydev servers list
sillydev status 1a7ce997
sillydev power 1a7ce997 restart
sillydev command 1a7ce997 "say hi"
sillydev deploy 1a7ce997
sillydev files ls 1a7ce997 /src
sillydev files cat 1a7ce997 /config.json
sillydev backups list 1a7ce997
sillydev backups create 1a7ce997 --name before-update
```

Add `--json` to any command for JSON output. `python -m sillydev` works too.

## MCP server

The MCP server lets an AI assistant (Claude Desktop, Claude Code, Cursor and others) check on and
control your servers. It needs Python 3.10 or later:

```sh
pip install 'sillydev[mcp]'
```

Then add it to your assistant's MCP settings, for example:

```json
{
  "mcpServers": {
    "sillydev": {
      "command": "sillydev-mcp",
      "env": { "SILLYDEV_API_KEY": "sdevc_..." }
    }
  }
}
```

Its tools are: list servers, server status, power action, send command, list files, read file,
Git deploy and list backups. There is nothing for billing or for deleting anything. Give it a key
limited to the servers and permissions you want the assistant to have.

## Configuration

| Option | Environment variable | Default |
| --- | --- | --- |
| `api_key` | `SILLYDEV_API_KEY` | (required) |
| `base_url` | `SILLYDEV_BASE_URL` | `https://sillydev.co.uk/panel/api/client` |
| `timeout` | | 60 seconds |
| `max_retries` | | 2 |

## License

MIT
