The Panelica MCP Server is an open-source Model Context Protocol server that turns a Panelica hosting panel's own API into 404 named tools, so Claude, Cursor, Codex CLI, Gemini CLI or any other MCP client can list domains, issue certificates, create databases or restart services through plain English instead of a browser session.
What Is an MCP Server?
The Model Context Protocol is an open standard that lets an AI assistant discover and call an application's real functionality as a list of named, typed tools, instead of guessing at an API from documentation it may or may not have read. An MCP server is the small program that exposes those tools: it speaks a JSON-RPC protocol over standard input and output to whatever client launches it, describes what each tool does and what arguments it takes, and executes the underlying request when the client calls one. The client, not the server, decides which tool to call and when — the server's job is to make that decision informed instead of guessed.
What Is the Panelica MCP Server?
The Panelica MCP Server (npm package panelica-mcp, current version 0.5.1) is Panelica's own implementation of that standard: it takes the External API that already powers the panel's dashboard and turns it into a tool an AI client can call directly, with the same HMAC-signed authentication, the same scoped permissions, and the same audit trail every other API consumer gets. It is open source under the MIT license, published on npm as panelica-mcp, as a container image at ghcr.io/panelica/panelica-mcp (tags latest and 0.5.1), and listed in the official MCP Registry as io.github.Panelica/panelica-mcp. The source lives at github.com/Panelica/panelica-mcp.
Under the hood the server is a thin, stateless adapter. It does not store your panel's data, does not cache responses across sessions, and does not send telemetry anywhere — every tool call becomes one signed HTTP request to your own panel and nothing else.
How Does the Panelica MCP Server Work End to End?
A single request travels through five hops between the moment your assistant decides to call a tool and the moment your panel actually restarts a service or writes a DNS record. The diagram below draws that path left to right.
Every tool call makes this same round trip; nothing is cached and nothing is executed anywhere except your own panel.
panelica-mcp binary over stdio — there is no separate server process to keep running unless you choose the Docker option.<PANELICA_BASE_URL>/v1/api-spec from your own panel and builds its tool list from that live specification, falling back to a bundled snapshot only if the panel is unreachable within the timeout.panelica-mcp builds the matching HTTP request, signs it with HMAC-SHA256 using your local PANELICA_API_SECRET, and forwards it through nginx on port 8443 to the panel's internal external-server process on 127.0.0.1:3002.Because the tool catalogue is generated from your panel's own /v1/api-spec at startup rather than hand-written once and shipped, a new Panelica release that adds an endpoint shows up as a new tool the next time an MCP client reconnects — nobody has to update the server's code to keep it accurate. Set PANELICA_LIVE_SPEC=0 if you would rather pin the server to its bundled snapshot instead of re-checking the live spec on every startup.
How Do I Get an API Key and Connect a Client?
Every client below needs the same three values, generated once in the panel.
pk_...) and the secret (sk_...). The secret is shown only once; store it in a password manager, not in a chat log.*:read can answer any "what is the current state of X" question and cannot change anything, no matter how the assistant is instructed. Add write and delete scopes only once you trust the workflow.
How Do I Connect Claude Code to My Panelica Panel?
Claude Code registers an MCP server with a single command:
claude mcp add panelica \
-e PANELICA_BASE_URL=https://your-panel:8443/api/external \
-e PANELICA_API_KEY=pk_... \
-e PANELICA_API_SECRET=sk_... \
-- npx -y panelica-mcp
Replace your-panel with your panel's hostname or IP, and the key and secret with the values from the step above. Ask Claude Code to "list my domains" and it drives the panel through that scoped key from the next message onward.
How Do I Connect Claude Desktop?
Edit the Claude Desktop config file — ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows, or ~/.config/Claude/claude_desktop_config.json on Linux (Claude Desktop beta):
{
"mcpServers": {
"panelica": {
"command": "npx",
"args": ["-y", "panelica-mcp"],
"env": {
"PANELICA_BASE_URL": "https://your-panel-host:8443/api/external",
"PANELICA_API_KEY": "pk_...",
"PANELICA_API_SECRET": "sk_..."
}
}
}
}
Save the file, fully quit Claude Desktop — not just close the window — and reopen it. A new chat shows panelica as a connected server with 37 tools available: the 34-tool core set plus the three meta tools. Set PANELICA_TOOLSETS=all in the same env block if you want every one of the 404 tools registered instead of the compact default.
How Do I Connect Cursor?
In Cursor, open Settings > MCP > Add new server and paste the same shape of configuration:
{
"panelica": {
"command": "npx",
"args": ["-y", "panelica-mcp"],
"env": {
"PANELICA_BASE_URL": "https://your-panel-host:8443/api/external",
"PANELICA_API_KEY": "pk_...",
"PANELICA_API_SECRET": "sk_..."
}
}
}
Cursor budgets active tools across every MCP server it has connected — roughly 40 total — and silently drops whatever does not fit. The default core toolset keeps Panelica's footprint at 37, comfortably inside that budget alongside whatever other MCP servers you already use. See the toolset section below if you need a specific category instead of the core set.
How Do I Connect OpenAI Codex CLI?
Add a server block to ~/.codex/config.toml:
[mcp_servers.panelica]
command = "npx"
args = ["-y", "panelica-mcp"]
[mcp_servers.panelica.env]
PANELICA_BASE_URL = "https://your-panel:8443/api/external"
PANELICA_API_KEY = "pk_..."
PANELICA_API_SECRET = "sk_..."
Codex CLI reads server instructions and tool annotations the same way Claude Code and Cursor do, so read-only tools are auto-approved and destructive ones ask for confirmation before they run.
How Do I Connect Gemini CLI, Continue.dev, Cline, Zed or Docker?
Any MCP-aware editor or CLI that can launch a stdio process — Gemini CLI, Continue.dev, Cline, Zed included — connects the same way: point it at npx -y panelica-mcp (or the absolute path to a built dist/index.js) with the same three environment variables, using whatever server-registration mechanism that particular tool already exposes. The command and the variables do not change between clients; only where you paste them does.
If you would rather run a long-lived container instead of letting the client spawn npx on demand, a pre-built image is published to GitHub Container Registry on every release:
docker run --rm -i \
-e PANELICA_BASE_URL=https://your-panel-host:8443/api/external \
-e PANELICA_API_KEY=pk_... \
-e PANELICA_API_SECRET=sk_... \
ghcr.io/panelica/panelica-mcp:latest
The -i flag keeps stdin attached so the client can talk to the container, and --rm removes it on disconnect. The image runs as a non-root user and exposes no ports — it speaks only stdio, the same as the npm binary.
What Does the Assistant Actually Know About My Panel?
An assistant that sees 404 bare tool names still has to guess ids, retry after 403s, and misread responses. The Panelica MCP Server hands it the API by heart instead, and every piece of that knowledge is generated from the same live catalogue, so it can never promise a route the panel does not have.
Server instructions and your key's identity, before the first call
On initialize, the server sends instructions every client injects into the model's own context: how ids work, that every user is called an account, the {"status":"success","data":...} response envelope, what each error class means, the category map, and workflow recipes — included only when every step in the recipe actually exists in the catalogue. Then, unless PANELICA_STARTUP_PROBE=0 is set, the server calls GET /v1/me once and tells the model which key it is holding, its scopes, its rate-limit tier and its expiry. A read-only key is announced as read-only from the first message; a rejected credential is reported in the instructions before the assistant tries a single tool.
A Returns line and id provenance on every tool
Each tool's description carries its HTTP route, the scopes it requires — including "one of A or B" rules and ROOT/ADMIN role limits where they apply — a Returns: line naming the response fields, and a risk class of read-only, mutating or destructive. Id parameters say exactly where their value comes from, for example "UUID of the domain — obtain it from GET /v1/domains," for path, query and body fields alike. That single sentence is why the assistant lists before it acts instead of inventing an id that looks plausible.
Enums so the model never guesses a value
Fields with a fixed set of allowed values carry a JSON-schema enum and a default straight from the panel's spec. ssl_provider is letsencrypt, self_signed or none, defaulting to letsencrypt; web_server is nginx_apache or nginx_only, defaulting to nginx_apache; a DNS record's type is one of A, AAAA, CNAME, MX, NS, PTR, SRV, TXT, CAA, TLSA or DS; an account's role is ADMIN, RESELLER or USER, defaulting to USER; a database user's role is read, readWrite, dbAdmin or dbOwner, defaulting to readWrite. Ask an assistant connected this way which values are valid for any of these fields and it answers from the schema, not from memory of some other panel's conventions.
Keeping big lists usable: _limit, _fields, _match
Every list-returning GET tool accepts three extra parameters the MCP server applies on your behalf — the panel itself never sees them: _limit caps how many items come back, _fields is a comma-separated list that trims each item to the fields you actually asked about, and _match does a case-insensitive substring filter. The response carries a _shaped {total, matched, shown} note so the model always knows whether it is looking at everything or a filtered slice. A panel with hundreds of domains is searchable in one call instead of a full unpaginated dump; oversized results that still exceed PANELICA_MAX_RESULT_CHARS (default 60000) are cut to the first items with an explicit _truncated note rather than silently flooding the context.
Errors it can act on, not just report
A 403 names the missing scope and states plainly that retrying will not help. A 404 says to re-list and use a real id. A 409 explains the conflict or that the feature is not configured. A 429 reports the exact reset window, and a window of 15 seconds or less is waited out once automatically rather than surfaced as a failure. A 5xx tells the assistant to report the problem, not loop on it. Gin validation failures are rewritten into plain statements like field "user_id" is required, using the same field name the model used in its call. A GET that fails on the network is retried once. A clock skew above two minutes between the machine running the MCP server and the panel is flagged directly in the instructions, since HMAC timestamps outside a five-minute window are rejected outright.
Three meta tools that reach the whole catalogue
Alongside whichever toolset is active, every connection gets three meta tools: panelica_find_tools, a keyword search across all 404 tools that understands the vocabulary people actually use — website maps to domain, certificate to ssl, mailbox to email, container to docker, and plurals are stemmed automatically; panelica_describe_tool, which returns one tool's exact parameters, body fields with type, required flag, enum and default, and its full response field list; and panelica_call, which executes any catalogue tool by name with the same scoped, HMAC-signed client the registered tools use. A client that discovers a tool name through panelica_find_tools can run it through panelica_call even if that tool was never directly registered.
Toolsets and the Cursor 40-tool cap
Every registered tool costs prompt tokens on every turn, and some clients — Cursor in particular — cap active tools across all connected MCP servers at roughly 40 and silently drop the rest. That is why the default toolset is core: 34 everyday tools spanning accounts, domains, DNS, SSL, databases, email, FTP, backups, server status and services, WordPress, Docker and plans, plus the three meta tools, for 37 total. Set PANELICA_TOOLSETS=all to register every catalogue tool — fine for Claude Code or Claude Desktop, which do not enforce the same cap — none to register only the three meta tools and reach everything through panelica_call, or a comma-separated list of category slugs such as domains,dns,ssl,git,docker,file_manager,laravel_apps,node_js_apps,python_apps,logs to load exactly the categories a given workflow needs.
What Can I Actually Ask It to Do?
Every prompt below maps to one or more real tools in the catalogue, each gated by the scope shown. A key without that scope declines the request and names what is missing instead of attempting it.
Accounts
- "List every hosting account and flag any that are suspended." —
panelica_accounts_get_v1_accounts, scopeaccounts:read. - "Create a new account for [email protected] on the Business plan." —
panelica_accounts_post_v1_accounts, scopeaccounts:write. - "Suspend the account for acme-corp until their invoice clears." —
panelica_accounts_post_v1_accounts_id_suspend, scopeaccounts:write.
Domains
- "Which domains exist on this panel and what PHP version does each use?" —
panelica_domains_get_v1_domains, scopedomains:read. - "Add the domain shop.example.com to the account for alice, using Let's Encrypt for SSL." —
panelica_domains_post_v1_domains, scopedomains:write. - "Switch example.com from PHP 8.2 to PHP 8.3." —
panelica_domains_patch_v1_domains_id_php, scopedomains:write. - "Add a subdomain blog.example.com pointing at the same document root." —
panelica_domains_post_v1_domains_id_subdomains, scopedomains:write.
DNS
- "List the DNS records of example.com and tell me whether it has an SPF record." —
panelica_dns_get_v1_dns_zones_domain_id_records, scopedns:read. - "Add an A record for mail pointing to 203.0.113.10." —
panelica_dns_post_v1_dns_zones_domain_id_records, scopedns:write. - "Update the TTL on the CNAME record for www to 3600." —
panelica_dns_patch_v1_dns_records_id, scopedns:write.
SSL
- "Show the SSL certificate status for example.com: issuer, expiry, auto-renew." —
panelica_ssl_get_v1_ssl_domains_domain_id, scopessl:read. - "Issue a Let's Encrypt certificate for shop.example.com." —
panelica_ssl_post_v1_ssl_domains_domain_id_issue, scopessl:write.
- "How many email accounts exist across the panel?" —
panelica_email_get_v1_email_accounts, scopeemail:read. - "Create a mailbox [email protected] with a 2GB quota." —
panelica_email_post_v1_email_accounts, scopeemail:write.
Databases
- "List all databases and which account owns each one." —
panelica_databases_get_v1_databases, scopedatabases:read. - "Create a MySQL database for the staging environment and hand me a read-write user." —
panelica_databases_post_v1_databases, scopedatabases:write.
Backups
- "List the backups available for example.com." —
panelica_backups_get_v1_backups, scopebackups:read. - "Trigger a fresh backup of that account before I touch anything." —
panelica_backups_post_v1_backups, scopebackups:write.
WordPress
- "List every WordPress install and flag the ones with a core update pending." —
panelica_wordpress_get_v1_wordpress. - "Update WordPress core on the site for example.com." —
panelica_wordpress_post_v1_wordpress_id_update_core. Askpanelica_describe_toolfor either tool's exact required scope before granting a key — it is generated from the panel's own middleware, not guessed.
Docker
- "List every running Docker container and how much memory each is using." —
panelica_docker_get_v1_docker_containers, scopedocker:read.
Server health
- "Give me a short server health summary: status, running services, load and memory." —
panelica_server_get_v1_server_status,panelica_server_get_v1_server_services,panelica_server_get_v1_server_metrics, scopeserver:read. - "Restart the MySQL service — it stopped responding." —
panelica_server_post_v1_server_services_name_restart, scopeservices:restart(a metrics-onlyserver:readkey cannot do this).
What Does a Real Session Look Like?
We drove the Panelica MCP Server against a development panel with OpenAI Codex CLI across two rounds of tasks, using keys scoped to *:read plus dns:write (one round did not yet include dns:delete; it was added before the second). The pattern held throughout: the agent never invented an id, it consulted panelica_describe_tool before writes, and it re-listed to verify destructive results instead of trusting a 200 response on its own. Read-only calls like the one below are auto-approved by clients that respect the readOnlyHint annotation, so the answer arrives with no confirmation prompt in the way.
Read-only tool calls are auto-approved by clients that respect the readOnlyHint annotation on every GET tool. Answer shown is the real, sanitised output from that session.
Six transcripts from that work, condensed to the tool calls and the final answer:
Across this work the agent used _fields, _match and _limit repeatedly without being told those parameters existed, and when asked which values were valid for ssl_provider and web_server it called panelica_describe_tool and confirmed the exact allowed values from the response rather than guessing. None of that behavior is scripted into the assistant. It falls directly out of the instructions, the Returns: lines, the id provenance and the enums described above — a model that reads carefully behaves carefully, and the catalogue is built to make careful reading pay off.
destructiveHint annotation specifically so a capable client asks before it deletes a domain, drops a database, or removes a backup. Review what a session is about to do before approving a destructive call, the same way you would review a shell command before running it as root.
Is It Safe to Connect an AI Assistant to My Hosting Panel?
It is safe in proportion to how the API key behind it is scoped. No scope is preselected when you generate a key in the panel, every tool is labeled read-only, mutating or destructive so capable clients can auto-approve reads and ask before destructive calls, and a key can be revoked the moment you no longer trust the session using it. The full threat model — over-broad tokens, destructive actions, prompt injection, and the specific controls that answer each — is covered in depth in a companion post on scoped API keys, HMAC signing and rate limits, linked below.
How Do I Troubleshoot a Connection That Is Not Working?
| Symptom | Likely cause | Fix |
|---|---|---|
| Client reports "0 tools available" | Server crashed at startup, usually a missing env var | Run panelica-mcp once from a shell with the three env vars set and read stderr directly |
401 MISSING_API_KEY | PANELICA_API_KEY not set or not passed through | Re-check the client config and restart the client after editing it |
401 INVALID_SIGNATURE | Wrong secret, or clock drift over 5 minutes | Verify NTP sync on both the MCP host and the panel host |
| Connect timeout on the base URL | The :8443/api/external suffix was left off the URL | Confirm with curl -sk $PANELICA_BASE_URL/health, which should return {"status":"ok"} |
| Tool result starts with "Panelica API error 403" and names a scope | The API key lacks that scope | Add the scope to the key in the panel — the assistant is already told not to retry |
| Tool description says "Schema not statically declared" | The endpoint binds a dynamic request body | Pass a free-form body object; the panel validates it and returns the missing field by name |
| TLS verification fails | The panel is using its self-signed certificate | Install a real certificate on the panel (Settings > SSL) rather than disabling verification client-side |
Frequently Asked Questions
Does the Panelica MCP Server work with any hosting panel?
No — it is built specifically for Panelica panels, talking to the panel's own External API on port 8443. It requires a running Panelica panel, version 1.0.193 or newer recommended, with the External API surface stable from 1.0.180 onward.
Do I need to install anything on the panel server itself?
No. The server runs on whatever machine your MCP client runs on — your laptop, a CI runner, wherever — and reaches the panel over the same HTTPS port (8443) the browser dashboard already uses. You do not need to open the internal port 3002 to the public internet.
Which runtime does it need?
Node.js 20 or newer for the npm install path, or Docker for the container path. Either produces the same stdio binary.
How many tools does it expose, and will that number change?
As of September 2026, version 0.5.1 exposes 404 tools across 51 categories, generated from 408 documented External API endpoints (4 non-tool endpoints — health, the spec endpoint itself, the Postman collection and websocket routes — are skipped). The count changes only when the panel's own External API surface changes, and in live mode a running server picks that up automatically from your panel's /v1/api-spec without waiting for a new npm release.
What happens if my Cursor MCP tool budget is already full with other servers?
Set PANELICA_TOOLSETS to a narrower slice — a specific category list like domains,dns,ssl — instead of the 37-tool core default, or to none to load only the three meta tools and reach the rest through panelica_call.
Can the assistant do something my API key is not allowed to do?
No. Every tool call becomes a normal HMAC-signed request against the same scope-checked External API any other client uses. A key without domains:write cannot create a domain no matter how the request is phrased, because the panel itself — not the MCP server — enforces the scope.
Does the server store or transmit anything about my panel to a third party?
No. It emits no telemetry, writes no cache, and contacts no third party. The API secret is read from the local process environment, used only to compute the HMAC signature, and never logged or written to disk.
My panel predates the live api-spec fields — will it still work?
Yes, with plainer tool descriptions. Response-field lists, id provenance sentences and enum values come from the panel's spec, which newer panels derive from their handler source at build time; an older panel still returns a working tool, just one that shows {status, data} without named fields underneath.
Can I run the tool catalogue offline, without hitting my panel at startup?
Yes — set PANELICA_LIVE_SPEC=0 to always use the bundled snapshot in tools/tools.json instead of fetching /v1/api-spec at startup.
What is the difference between the Panelica MCP Server and OpsAI?
The MCP server connects external clients you already use — Claude Code, Cursor, Codex CLI — to your panel through a scoped API key from outside the panel entirely. OpsAI is a separate product surface: an AI desktop built into the panel itself, running as an isolated system user, described in a companion post linked below.
Is there a limit to how many tool calls an assistant can make per minute?
Yes — the API key's rate-limit tier governs that, not the MCP server. Starter keys allow 60 requests per minute, professional 300, business 1000, and enterprise is unlimited. A 429 response reports exactly when the window resets, and the assistant is instructed to wait out short windows automatically rather than fail the task.