Skip to main content
Glama

@bippy-s-ai-helpers/pterodactyl-mcp

A stdio MCP (Model Context Protocol) server for the Pterodactyl game panel, covering both API trees in one server:

  • the client tree (/api/client, authenticated with a ptlc_ client API key), and

  • the application tree (/api/application, authenticated with a ptla_ application API key),

speaking plain JSON-RPC 2.0 over stdio: no network listener, no HTTP framework, no config files. The MCP client spawns this binary and talks to it over stdin and stdout; every HTTP request happens inside the server process toward the panel.

Features

  • 105 tools over both Pterodactyl API trees (65 client tree, 40 application tree at panel v1.15.x): account surfaces, server core, databases, backups, schedules and tasks, network allocations, files (including upload, download, and raw contents), startup variables, settings, subusers, an 8-tool console suite over the wings websocket, and the admin tree (users, nodes and allocations, locations, servers, server databases, nests and eggs).

  • Default-deny destructive gate: 21 tools classified as destructive (deletes, reinstalls, suspends, power kill, and the subuser access-grant trio) are registered but deny by default with zero panel requests until you explicitly allowlist them.

  • Tree toggles to control context cost: expose only the client tree, only the application tree, or both. Every disabled family registers nothing, so a partial deployment keeps the tools/list payload small.

  • Lean dependency set: the MCP SDK, ws, and zod only. No HTTP framework, no dotenv, no runtime config files.

  • Deterministic generated docs: docs/endpoint-reference.md (every mapped and doctrine-excluded panel endpoint with tool attribution, gate class, and route-file citation) and docs/census.md (registry counts, LOC census, test counts) ship in the repo and regenerate byte-identically with npm run docs.

  • Strictly typed client core: Fractal-envelope parsing, typed error classes for 401/403, 404, 422, 429, 5xx, non-JSON bodies, and network failures, pagination with a hard page cap, and a console bridge with a bounded in-memory tail ring.

Related MCP server: PterodactylMCP

Prerequisites

  • Node.js 18 or newer and npm.

  • A reachable Pterodactyl panel (v1.15.x; built and tested against 1.15.1).

  • An application API key (ptla_ prefix) for the admin tree, and/or a client API key (ptlc_ prefix) for the client tree; at least one is required, and only the tree(s) whose key you configured are exposed.

  • An MCP-capable client (opencode works for sure) that can spawn a local stdio MCP server.

Installation (humans)

Step 1: get the code

Either clone with git:

git clone https://github.com/Bippy-s-AI-Helpers/pterodactyl-mcp.git
cd pterodactyl-mcp

Or download the repository as a zip / copy the tree manually and cd into the folder.

Step 2: install and build

npm install
npm run build

npm run build compiles src/ (TypeScript, strict, NodeNext) into dist/index.js; that file is the entrypoint your MCP configuration below launches.

npm scripts

Script

What it does

npm run build

TypeScript build (strict, NodeNext) into dist/

npm run typecheck

tsc --noEmit over src, test, and scripts

npm run test

the full vitest suite (mock panel + stub wings socket, loopback only)

npm run lint

eslint over src, test, scripts

npm run smoke

spawns the real dist/index.js and verifies the JSON-RPC handshake, the exact 105-tool registry, the 21 gate annotations, and the description/annotation doctrine

npm run docs

regenerates docs/endpoint-reference.md and docs/census.md (deterministic; reruns are byte-identical)

npm run verify

build, then typecheck, lint, test, smoke in that order

Step 3: configure your MCP client (no keys pasted into config)

opencode style entry

Values that carry secret material reference files instead of inlining their contents; see the key handling note below the table.

"mcp": {
  "pterodactyl": {
    "type": "local",
    "command": ["node", "~/.config/opencode/repos/pterodactyl-mcp/dist/index.js"],
    "enabled": true,
    "environment": {
      "PTERODACTYL_URL": "https://panel.example.com",
      "PTERODACTYL_CLIENT_KEY": "{file:~/.config/opencode/secrets/PTERODACTYL_CLIENT_KEY}",
      "PTERODACTYL_APPLICATION_KEY": "{file:~/.config/opencode/secrets/PTERODACTYL_APPLICATION_KEY}",
      "PTERODACTYL_TREES": "client,application"
    }
  }
}

Contract notes: type is required (local); command is an array of strings, never a single string; enabled gates the whole entry; use an absolute path to dist/index.js if your client does not expand ~.

generic JSON MCP client entry

{
  "mcpServers": {
    "pterodactyl": {
      "command": "node",
      "args": ["/absolute/path/to/pterodactyl-mcp/dist/index.js"],
      "env": {
        "PTERODACTYL_URL": "https://panel.example.com",
        "PTERODACTYL_CLIENT_KEY": "your-client-api-key",
        "PTERODACTYL_APPLICATION_KEY": "your-application-api-key",
        "PTERODACTYL_TREES": "client,application"
      }
    }
  }
}

For generic clients the two standard patterns are: environment variables exported into the client's process environment before it spawns the server, or a dotenv-style file that the loading shell (or the client, if it supports envFile) applies before launch. Never commit such a file; see SECURITY.md.

Environment variables

The server reads configuration from the environment only. There is no dotenv and no config file; a dotenv-style loader invoked by the launching shell is fine, but the server itself never opens one.

Variable

Required

Default

Meaning

PTERODACTYL_URL

yes

none

Panel base URL, absolute http or https, trailing slash trimmed

PTERODACTYL_CLIENT_KEY

one key minimum

none

ptlc_ client API key for the /api/client tree; a wrong prefix fails startup with a clear error

PTERODACTYL_APPLICATION_KEY

one key minimum

none

ptla_ application API key for the /api/application tree; a wrong prefix fails startup

PTERODACTYL_TREES

no

every tree whose key is set

Comma list of client and/or application to expose; an unknown name or a tree without its key fails startup

PTERODACTYL_DESTRUCTIVE_ALLOWLIST

no

empty (deny all destructive)

Comma list of gated tool names permitted past the destructive gate; unknown names are dropped with one warning and can never enable anything; a change requires a restart

PTERODACTYL_ALLOW_KILL

no

false

Permits the kill power action; see the gate section. Accepts true, 1, yes (and the false spellings false, 0, no, empty)

PTERODACTYL_TIMEOUT_MS

no

30000

Per request HTTP timeout in whole milliseconds (positive integer)

PTERODACTYL_CONSOLE_TAIL_LINES

no

500

Console read window tail cap in lines (whole number between 1 and 5000)

Startup prints exactly one redacted configuration line to stderr (key prefixes and lengths only, plus the tree list); key material is never logged, echoed into errors, or returned by any request path. stdout carries only JSON-RPC. A failed startup prints one line per configuration error to stderr and exits 1.

Key handling

  • Keys enter the server only through environment variables, at spawn time from your MCP client.

  • In opencode, the supported pattern is a {file:PATH} reference: opencode reads the file at load time, trims it, and substitutes the raw value for the entry (a missing file fails the config load loudly, which is the intended behavior). Keep the key files themselves out of any repository and chmod them 0600 (and 0700 on the containing directory).

  • In generic MCP clients, pass the key values in the env map of the entry, or export them into the client's process environment (a dotenv-style file sourced by your shell works; keep it out of every repository, keep it 0600).

  • The raw key values never appear in any configuration you commit, in any log, in any error message, or in any tool result. The startup line prints only the prefix and the length of each configured key. Key material moves strictly inside the Authorization header of panel requests, so panel error bodies can never quote it back.

  • Changing any entry your client uses to launch this server (including any {file:} referenced key file) requires the client to reload MCP configuration, and in a typical MCP client configuration is read once at startup: after changing the entry or the key files, restart the client (in opencode, a full restart) before the change is live.

Agentic installation (fresh build agent, follow verbatim)

  1. Read this entire README section, plus SECURITY.md, before touching anything.

  2. Create the install directory and fetch the repo: mkdir -p ~/.config/opencode/repos && cd ~/.config/opencode/repos && git clone https://github.com/Bippy-s-AI-Helpers/pterodactyl-mcp.git && cd pterodactyl-mcp. If the host has no git remote access, copy the repository tree manually into that directory with cp -r from wherever the source is available.

  3. Run npm install (network access to the npm registry is required for the dependencies only).

  4. Run npm run build.

  5. Run npm run smoke and require exit code 0 (it validates handshake, tool registry, gate annotations, and redaction; no panel is touched).

  6. STOP HERE at the user breakpoint. The two API keys are user-supplied material: never fabricate, never echo, never write key-shaped values of your own. Editing the user's MCP client configuration and restarting the client are user-owned steps. Do not create the mcp entry, do not set PTERODACTYL_DESTRUCTIVE_ALLOWLIST or PTERODACTYL_ALLOW_KILL yourself, and do not restart the client. Report the built path (the absolute path of dist/index.js) and hand the user steps 7 to 9.

  7. (User) install the two panel API key files at ~/.config/opencode/secrets/PTERODACTYL_CLIENT_KEY and ~/.config/opencode/secrets/PTERODACTYL_APPLICATION_KEY (raw values, 0600, 0700 directory).

  8. (User) add the mcp entry from the installation section above, with the absolute path from your report.

  9. (User) restart the client and confirm the server answers; the one redacted line on stderr confirms the loaded configuration.

Tool catalog summary and tree toggles

The registry closes at 105 tools:

Tree

Family

Tools

client

account, activity list, permissions dictionary, servers list (the api-key, two-factor, ssh-keys, and account credential routes are doctrine-excluded)

4

client

server core (server view, resources, activity, stdin command, power)

5

client

databases

4

client

backups

7

client

schedules + tasks

9

client

network (allocations)

5

client

files (incl. upload, download URL, raw contents)

13

client

startup (variables)

2

client

settings

3

client

console suite (websocket bridge)

8

client

subusers

5

admin

users

6

admin

nodes + allocations

9

admin

locations

5

admin

servers

11

admin

server databases

5

admin

nests + eggs

4

Total: 65 client tree + 40 application tree = 105.

PTERODACTYL_TREES semantics (code truth, src/config.ts):

  • unset: every tree whose key is set is exposed (client only with just a client key; both with both keys).

  • set to a comma list of client and/or application: exactly those trees are exposed; each named tree must have its key configured, and an unknown name is a startup error.

  • tree gating happens at registration: a disabled family registers no tools at all, which shrinks tools/list and the context your client spends on it.

The 21 gated tools (deny by default)

All 21 register even while denied (discoverable failure: the call returns a structured denial naming the enabling variable, with zero panel requests). 13 client tree:

server_power (kill sub-flag), database_delete, backup_restore (truncate sub-flag), backup_delete, schedule_delete, schedule_task_delete, allocation_delete, files_delete, files_pull, settings_reinstall, subuser_create, subuser_update, subuser_delete (the access-grant trio is gated because granting panel access is consequential regardless of the HTTP verb).

8 application tree: admin_user_delete, admin_node_delete, admin_node_allocation_delete, admin_location_delete, admin_server_suspend, admin_server_reinstall, admin_server_delete (the force variant is covered by the tool's single allowlist entry), admin_server_database_delete.

The complete endpoint table (all 111 panel endpoints: method, path, tool, gate class, exclusion reason, citation) is generated at docs/endpoint-reference.md; registry and test counts live in docs/census.md.

Security model

  1. Transport: stdio only. The server never opens a network listener; the MCP client spawns it as a child process.

  2. Tree gate at startup: a tool family registers only when its tree is enabled (PTERODACTYL_TREES plus the key present).

  3. Discoverable denial: every gated tool that its tree permits is registered even while denied; calling it answers with a structured denial naming the enabling variable and makes zero HTTP requests.

  4. Immutable gate snapshot: every gate decision reads a snapshot resolved once at startup, never the runtime environment, so env changes mid-session cannot flip a gate. Fail-closed: unknown or malformed allowlist entries never enable anything and are reported once at startup.

  5. Annotations as hints, never enforcement: every tool carries accurate readOnlyHint / destructiveHint / idempotentHint annotations; the gate decides before annotations could matter.

PTERODACTYL_DESTRUCTIVE_ALLOWLIST semantics: by default empty and every gated tool denies. Entries must name one of the 21 gated tool names above; anything else (a read or write tool's name, a typo, an unknown family tool) is dropped with exactly one stderr warning and can never enable anything. The allowlist is restart-grade: after changing it, restart the MCP server (restart your MCP client).

PTERODACTYL_ALLOW_KILL semantics: the kill action of server_power additionally requires this sub-flag true even when server_power itself is allowlisted; start, stop, and restart do not need it. The snapshot is read once at startup, so enabling it is also restart-grade.

TLS verification status of the console bridge: the ticket exchange runs through the normal client-tree REST client, and the wings upgrade is a plain wss/ws connection with the library default TLS verification left strictly on (no rejectUnauthorized: false and no certificate-relaxing environment variable exists anywhere in the code). A wrong or self-signed certificate fails loudly as a network error.

Keys and tickets: key material exists only in the environment and inside request headers to the panel; the startup line prints prefixes and lengths only; error messages carry method, path, status, and a truncated panel message (never headers). Console tickets (short-lived JWTs from the panel) exist only in process memory while a console session is connected. Console sessions and their bounded tail rings live in memory for the server process lifetime; nothing is persisted to disk.

Testing posture

  • Mock-first: every vitest suite runs against the in-process mock panel and stub wings websocket server on loopback (test/mock/); no test touches a real panel.

  • The two route file copies under test/fixtures/ are byte-identical captures of the panel's route files with pinned sha256 digests; the drift test reconciles all 111 endpoints, method, path, tree, and citation against the route table, so a panel update that changes routes fails with the named row.

  • npm run smoke runs the real dist/index.js over real stdio with fake key shapes and asserts the handshake, the exact 105-tool registry, the 21 gate annotations, and the short-description doctrine (every tool describes when to call it, and carries annotations).

  • scripts/live-smoke.mjs is the read-only battery driver for a run against a real panel; it reads key material only from the files named by PTERO_KEY_FILE_APP and PTERO_KEY_FILE_CLIENT, never prints values, appends a defensive redactor between every transcript write, and its default battery is strictly read-only (zero panel state changed). Point it at the mock first (scripts/mock-panel-start.mjs boots the same mock the test suite uses) before you ever aim it at a real panel.

Troubleshooting

Startup errors (each prints one stderr line, exit 1):

  • PTERODACTYL_URL is required (panel base URL, for example https://panel.example.test) : the URL variable is missing or empty.

  • PTERODACTYL_URL must be an absolute http or https URL (got: ...) : malformed URL.

  • PTERODACTYL_CLIENT_KEY must start with ptlc_ (got a different prefix, length N) (and the mirrored ptla_ wording for the application key) : the variables were swapped or truncated. Verify each key against Pterodactyl's key prefix conventions (ptlc_ is a client key, ptla_ an application key).

  • At least one API key is required: PTERODACTYL_CLIENT_KEY (ptlc_, client tree) and/or PTERODACTYL_APPLICATION_KEY (ptla_, application tree) : both missing.

  • PTERODACTYL_TREES has an unknown tree name (got: X; expected: client, application) : typo in the tree list.

  • PTERODACTYL_TREES names the client tree but PTERODACTYL_CLIENT_KEY is not set (and the application-tree mirror) : the tree list names a tree with no key configured.

  • pteromcp gating: dropped unknown PTERODACTYL_DESTRUCTIVE_ALLOWLIST entries (...) : one warning line; the dropped names never enable anything. Fix the names to the gated tool names above.

  • In opencode a missing {file:...} key file fails the config load loudly at startup: restore the file (raw value only) and restart.

Runtime:

  • A gated tool call answers DENIED (destructive gate): "<tool>" is registered but its gate class is G (destructive), request row <method> <path>, citation <route line>. By default PTERODACTYL_DESTRUCTIVE_ALLOWLIST is empty and every gated tool is denied. To enable exactly this tool, set PTERODACTYL_DESTRUCTIVE_ALLOWLIST to a comma list containing "<tool>" and restart the MCP server. No panel request was made. : expected default-deny behavior; add the tool to PTERODACTYL_DESTRUCTIVE_ALLOWLIST and restart the client.

  • DENIED (kill sub-gate): the "kill" action of "server_power" additionally requires PTERODACTYL_ALLOW_KILL=true ... : allowlisting server_power covers start, stop, and restart; killing additionally needs the sub-flag.

  • 401/403 answers from the panel surface as authentication errors (PteroAuthError-class text with method, path, and status): the key is wrong, expired, or lacks the subuser permissions the route needs.

  • 429 answers surface as rate-limit errors carrying the panel's Retry-After hint.

  • 502-style HTML from a reverse proxy answers as a non-JSON-body error (the guard exists exactly for that case).

  • Certificate failures on the console websocket surface as console connect network errors; the bridge never relaxes TLS verification.

  • MCP config loads at client startup only: after any server-side env change, restart the client before retesting.

License

MIT; see LICENSE and CHANGELOG.md.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    Not graded
    maintenance
    MCP server for Pterodactyl and Pelican game server panels. 73 tools for server management, power control, file operations, backups, schedules, databases, users, nodes, and eggs.
    34
    14 npm
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    An MCP server that lets AI clients manage game servers hosted on a Calagopus panel: list and inspect servers, read logs and files, edit configs, run console commands and power actions, and search, install, update, or remove plugins and mods from Modrinth and CurseForge. Every action is bounded by the user's panel API key and server permissions, with read-only/destructive hints and token-efficient tool responses.
    MIT