Skip to main content
Glama
README.md
# MCPDactyl

MCP server for the [Pterodactyl Panel](https://pterodactyl.io/), versions 1.x and 2.0. Manage servers,
files, databases and more across **multiple panels** from one MCP server. Runs over **stdio** and
**Streamable HTTP**, locally, in Docker, or as a remote server on Cloudflare Workers.

Panels are not baked into the install config — you add them at runtime and switch between them.
MCPDactyl detects whether a panel runs 1.x or 2.0 and offers the tools that fit it.

> Status: v0.1. The full Client API and Application (admin) API for 1.x and 2.0, plus the 2.0 Admin
> API (~230 tools on a 2.0 panel). 2.0 support follows the 2.0 beta documentation and has not been run
> against a live 2.0 panel yet; 1.x is the better-exercised path. See [ARCHITECTURE.md](./ARCHITECTURE.md) for the design.

## Install

Needs Node 20+ and [pnpm](https://pnpm.io/).

```bash
git clone https://github.com/MrMirhan/mcpdactyl.git
cd mcpdactyl
pnpm install
pnpm build
```

## Run (stdio)

```bash
node dist/index.js
```

Register it with an MCP client, using the absolute path of your clone. Claude Code:

```bash
claude mcp add mcpdactyl -- node /path/to/mcpdactyl/dist/index.js
```

Any client that reads an `mcpServers` config (Claude Desktop, Cursor, …):

```json
{
  "mcpServers": {
    "mcpdactyl": { "command": "node", "args": ["/path/to/mcpdactyl/dist/index.js"] }
  }
}
```

Then add a panel from the chat with `panel_add` (see [Panels](#panels)).

## Run (Streamable HTTP)

```bash
node dist/index.js --http --port 3000 --host 127.0.0.1
# endpoint: http://127.0.0.1:3000/mcp  (POST/GET/DELETE)
```

Each MCP session gets its own active-panel state; saved panels are shared via the config file.
Set `MCPDACTYL_HTTP_TOKEN` to require `Authorization: Bearer <token>` on every request. Bind to a
public host only behind auth/TLS — the panel registry is shared across clients (or use
`MCPDACTYL_STATELESS=1` so every call must carry inline creds).

A caller that acts for different people can pin each session to one panel instead: send
`x-ptero-base-url` and `x-ptero-client-key` and/or `x-ptero-application-key` headers on the
initialize request. That session talks to that panel only; nothing is read from or written to the
shared store, and no key appears in a tool argument. With `MCPDACTYL_SEALED=1` the tools also stop
accepting `baseUrl`/`clientKey`/`applicationKey` arguments and `panel_add`, `panel_remove` and
`panel_use` are not registered.

### Docker (HTTP)

```bash
docker build -t mcpdactyl .
docker run -p 3000:3000 -e MCPDACTYL_HTTP_TOKEN=your-secret mcpdactyl
```

The image is a self-contained bundle (no `node_modules`). Seed panels with
`-e MCPDACTYL_PANELS='[{"name":"prod","baseUrl":"https://panel","clientKey":"ptlc_…"}]'`
or mount a config dir at `~/.config/mcpdactyl`. Always set `MCPDACTYL_HTTP_TOKEN` when exposing the port.
The image serves the client tools by default; add `-e MCPDACTYL_TOOLS=all` for the admin tools too.

### Cloudflare Workers (remote, free tier)

`worker/index.ts` runs MCPDactyl as a remote MCP server via the `agents` `McpAgent` on a Durable
Object (per-session active panel) with Workers KV for the shared panel registry. The WebSocket
console tool is omitted on Workers (`ws` is Node-only). All other tools are identical — they reuse
the same `pteroRequest` and tool modules.

```bash
npx wrangler kv namespace create PANELS   # replace REPLACE_WITH_YOUR_KV_NAMESPACE_ID in wrangler.jsonc
npx wrangler deploy                        # needs a Cloudflare account (free plan is enough)
```

Endpoint: `https://<worker>.workers.dev/mcp` (Streamable HTTP). Choose the surface with the
`MCPDACTYL_TOOLS` var (`wrangler.jsonc` ships with `all`); seed read-only panels with an `MCPDACTYL_PANELS` secret or let clients
`panel_add` at runtime (persisted to KV).

**Protect the public endpoint with a bearer token.** Set `MCPDACTYL_HTTP_TOKEN` as a secret and every
request must send `Authorization: Bearer <token>` — otherwise anyone who reaches the URL can use
stored panel keys:

```bash
npx wrangler secret put MCPDACTYL_HTTP_TOKEN   # paste a long random token
```

Claude's built-in remote-MCP connector expects OAuth, so for a bearer connect through `mcp-remote`,
which injects the header:

```jsonc
{ "mcpServers": { "mcpdactyl": {
  "command": "npx",
  "args": ["mcp-remote", "https://<worker>.workers.dev/mcp", "--header", "Authorization: Bearer <token>"]
}}}
```

`pnpm cf:check` validates the build without deploying; `pnpm cf:dev` runs it locally.

Free-tier fit: Workers 100k req/day, Durable Objects on the free plan (SQLite), KV 100k reads + 1k
writes/day — ample for a personal proxy. CPU limit (10ms/invocation) is fine because the work is
I/O-bound (`fetch` to the panel).

## Panels

No URL or key in the MCP config. Manage panels via tools:

- `panel_add { name, baseUrl, clientKey?, applicationKey?, apiVersion?, makeDefault?, test? }`
- `panel_list` — keys masked
- `panel_use { name }` — active panel for this session
- `panel_test { name? }` — validate keys and re-detect the panel's API version
- `panel_remove { name }`

Every panel-targeting tool also accepts `panel` (a saved name) or inline `baseUrl` + `clientKey`/`applicationKey`.
Resolution order: **inline > panel name > active > default**.

Client API keys start with `ptlc_`, Application (admin) keys with `ptla_`. A tool fails clearly if the
selected panel lacks the key type it needs.

### Panel versions (1.x and 2.0)

Each panel's API version is detected the first time a version-bound tool needs it, from whether
`/api/admin/version` exists (2.0) or not (1.x). With only an application key the check uses
`/api/application/tags`. The answer is cached on the panel (`detectedApiVersion`); `panel_test`
probes again, for a panel upgraded since. Pin it with `apiVersion: "1"` or `"2"` in `panel_add`
(or in `MCPDACTYL_PANELS`) to skip detection.

The version decides what is offered and how a call is sent:

- 1.x: nest tools (`admin_nest_list`, `admin_nest_get`) are listed. `admin_egg_list` without `nest` collects every egg through the nests.
- 2.0: tag tools (`admin_tag_list`, `admin_tag_get`), `admin_node_deployable` and the whole Admin API (`adminapi_*`, 115 tools) are listed. `admin_egg_list` and `admin_egg_get` use the flat egg routes, and a `nest` id is mapped to its tag through `legacy_nest_id`.
- Unknown (not detected yet): both small sets are listed and the Admin API is not.

The tool list follows the active panel and the client is sent `tools/list_changed` when it changes.
A tool that needs one version also checks it when called, since a call can name another panel,
and says which version the panel runs. On 2.0 a root administrator's client key also works where
an application key is asked for.

### Storage & env

- Panels persist to `~/.config/mcpdactyl/panels.json` (chmod `600`).
- `MCPDACTYL_CONFIG_DIR` — override the config directory.
- `MCPDACTYL_PANELS` — JSON seed read at boot, never written to disk.
- `MCPDACTYL_STATELESS=1` — disable persistence; every call must carry inline creds.
- `MCPDACTYL_TRANSPORT=http|stdio`, `MCPDACTYL_HTTP_HOST`, `MCPDACTYL_HTTP_PORT`, `MCPDACTYL_HTTP_TOKEN` — HTTP transport.
- `MCPDACTYL_SEALED=1` — hide the credential arguments and the panel-switching tools (see [Run (Streamable HTTP)](#run-streamable-http)).
- `MCPDACTYL_TOOLS=client|admin|all` — which API surface to expose (default `client`). Application tools need a `ptla_` key (or, on 2.0, a root admin's `ptlc_` key); the Admin API needs a root admin's `ptlc_` key.

## Tools

Panel: `panel_add`, `panel_list`, `panel_use`, `panel_test`, `panel_remove`.

Full Client API (all accept `panel` / inline creds):

- Servers: `server_list`, `server_get`, `server_resources`, `server_power`, `server_command`, `server_activity`
- Settings: `server_startup_get`, `server_set_variable`, `server_rename`, `server_reinstall`, `server_docker_image`
- Console: `server_console_tail` (WebSocket, bounded capture window)
- Files: `files_list`, `files_read`, `files_write`, `files_rename`, `files_copy`, `files_mkdir`, `files_delete`, `files_compress`, `files_decompress`, `files_chmod`, `files_pull`, `files_download_url`, `files_upload_url`
- Databases: `db_list`, `db_create`, `db_rotate_password`, `db_delete`
- Schedules: `schedule_list`, `schedule_get`, `schedule_create`, `schedule_update`, `schedule_delete`, `schedule_execute`, `schedule_task_create`, `schedule_task_update`, `schedule_task_delete`
- Network: `network_list`, `network_assign`, `network_set_primary`, `network_set_notes`, `network_delete`
- Backups: `backup_list`, `backup_create`, `backup_get`, `backup_download_url`, `backup_restore`, `backup_toggle_lock`, `backup_delete`
- Subusers: `subuser_list`, `subuser_get`, `subuser_create`, `subuser_update`, `subuser_delete`, `permissions_list`, and `subuser_set_files` (only for panels running an SFTP-permissions addon; it is a route the stock panel does not have)
- Account: `account_get`, `account_activity`, `account_2fa_get`, `account_2fa_enable`, `account_2fa_disable`, `account_update_email`, `account_update_password`, `account_apikey_list`, `account_apikey_create`, `account_apikey_delete`, `account_sshkey_list`, `account_sshkey_create`, `account_sshkey_remove`

Application API (admin, `ptla_` key; opt-in via `MCPDACTYL_TOOLS=admin` or `all`):

- Users: `admin_user_list`, `admin_user_get`, `admin_user_get_external`, `admin_user_create`, `admin_user_update`, `admin_user_delete`
- Servers: `admin_server_list`, `admin_server_get`, `admin_server_get_external`, `admin_server_create`, `admin_server_update_details`, `admin_server_update_build`, `admin_server_update_startup`, `admin_server_suspend`, `admin_server_unsuspend`, `admin_server_reinstall`, `admin_server_delete`
- Server databases: `admin_server_db_list`, `admin_server_db_get`, `admin_server_db_create`, `admin_server_db_reset_password`, `admin_server_db_delete`
- Nodes: `admin_node_list`, `admin_node_get`, `admin_node_deployable`, `admin_node_config`, `admin_node_create`, `admin_node_update`, `admin_node_delete`, `admin_node_alloc_list`, `admin_node_alloc_create`, `admin_node_alloc_delete`
- Locations: `admin_location_list`, `admin_location_get`, `admin_location_create`, `admin_location_update`, `admin_location_delete`
- Nests & eggs: `admin_nest_list`, `admin_nest_get` (Pterodactyl 1.x only), `admin_egg_list`, `admin_egg_get` (pass `nest` on 1.x; omit it on 2.0)
- Tags (Pterodactyl 2.0, replace nests): `admin_tag_list`, `admin_tag_get`

Admin API (Pterodactyl 2.0 only, a root administrator's `ptlc_` key; `MCPDACTYL_TOOLS=admin` or `all`):
one `adminapi_*` tool per operation of `/api/admin`: users, servers (mounts, transfer, rebuild, suspension, databases,
backups), nodes (allocations, tags, utilization, deploy token), locations, mounts, eggs (variables, scripts, export,
import, catalog), tags, settings, database hosts, API keys, extensions and the panel version. The tools are generated from
the OpenAPI document of the 2.0 docs: `node scripts/gen-admin-api.mjs [openapi-v2.json | URL]` rewrites
`src/tools/admin/definitions.ts`. Query filters such as `filter[name]` are arguments named `filter_name`. Uploads
(`adminapi_install_extension`, `adminapi_import_egg`, `adminapi_update_egg_from_import`) take the file as base64, or the
JSON text itself for an egg.

## Develop

```bash
pnpm typecheck         # tsc --noEmit
pnpm typecheck:worker  # the same for the Cloudflare Worker entry
pnpm lint              # biome
pnpm test              # vitest: request client, panel registry, version detection, tools, Admin API
pnpm build             # tsup -> dist/index.js
pnpm smoke             # end-to-end stdio test, no real panel needed
pnpm smoke:http        # end-to-end Streamable HTTP test
pnpm cf:check          # Worker build dry-run
```

CI (`.github/workflows/ci.yml`) runs all of these. `node scripts/gen-admin-api.mjs` regenerates the
Admin API tools from the 2.0 OpenAPI document.

## License

MIT, see [LICENSE](./LICENSE).

TDQS

C2.6/5.0

Scored across 75 tools

Disambiguation4/5

Tools are generally cleanly separated by resource and action, and adjacent tools like subuser_update vs subuser_set_files are explicitly distinguished. A few pairs remain close enough to require description-checking (files_pull vs files_upload_url, backup_download_url vs files_download_url, panel_list vs panel_test), but most boundaries are clear.

Naming Consistency4/5

Names consistently use snake_case and a resource-prefixed pattern (backup_*, files_*, schedule_*, account_*). Minor deviations exist where the action is implicit or noun-like, such as server_resources, server_power, server_activity, and files_mkdir, but the convention remains readable and predictable.

Tool Count1/5

At 75 tools, this surface is far beyond a reasonable tool count for an agent-facing MCP server and creates a heavy selection burden. The broad Pterodactyl management scope justifies many operations, but not this many in a single toolset.

Completeness4/5

Coverage is extensive across files, backups, schedules, network allocations, subusers, panels, and account management, with full lifecycle operations for most resources. Minor gaps remain around server-level creation/deletion or transfer/admin operations, but core workflows have no dead ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues