What's new
Panelica Community Forum

Welcome to the official Panelica Community Forum — the central hub for server administrators, developers, and hosting professionals. Register a free account today to access technical discussions, product announcements, feature requests, and direct support from the Panelica team. Be part of the growing community shaping the future of server management.

Panelica MCP Server — complete guide: setup for every client, what the assistant can do, scopes, examples, troubleshooting

admin

Administrator
Staff member
Panelica MCP Server — complete guide​

The Panelica MCP Server (package panelica-mcp) is an open-source Model Context Protocol server that turns your Panelica panel into a set of tools an AI assistant can call directly — list and create accounts, provision domains, manage DNS, issue SSL certificates, create databases and mailboxes, run backups, inspect Docker containers, and check server health — through the same Panelica External API your panel UI already uses. It runs as a small stdio process next to your MCP client (Claude Code, Claude Desktop, Cursor, OpenAI's Codex CLI, Continue.dev, Cline, Zed, or any other stdio MCP client), signs every request to your panel with HMAC-SHA256, and never sends your API secret anywhere but your own panel. As of September 2026, version 0.5.1 ships 404 tools across 51 categories, with a 37-tool core set registered by default.

This thread is the reference: every client's setup steps, the full security model, the scope table, 20+ example prompts, a real measured agent session, and troubleshooting. If you just want the two-minute version, see the announcement thread and the "Connect Claude Code in 60 seconds" thread linked at the bottom.

What is the Panelica MCP Server?​

It is a thin, stateless adapter — no database of its own, no telemetry, nothing cached to disk. What it does:

  1. Your MCP client launches the
    Code:
    panelica-mcp
    binary over stdio.
  2. At startup, the server fetches
    Code:
    <PANELICA_BASE_URL>/v1/api-spec
    from your panel and builds the tool list from it — this is the live catalogue, and it is the default. If the panel is unreachable, it falls back to a bundled snapshot (
    Code:
    PANELICA_LIVE_SPEC=0
    forces the snapshot always).
  3. The client asks for the tool list and gets it back together with server instructions — how ids work, how scopes work, what the response envelope looks like, what to do with each error class, and workflow recipes — all generated from that same catalogue.
  4. When the client calls a tool, the server builds the matching HTTP request, signs it with HMAC-SHA256 using your local
    Code:
    PANELICA_API_SECRET
    , and sends it to your panel's External API.
  5. The HTTP response comes back to the client as the tool result. A failed call is returned as an explanation of what to do next — missing scope, wrong id, rate-limit reset — not a bare status code.

The transport chain looks like this:

Code:
MCP client (Claude, Cursor, Codex...)
   |  stdio JSON-RPC
   v
panelica-mcp (this package, runs on your machine or in Docker)
   |  HTTPS + HMAC-SHA256 (X-API-Key, X-Timestamp, X-Signature)
   v
https://your-panel-host:8443/api/external/v1/...
   |  nginx on the panel host (TLS + strips /api/external prefix)
   v
127.0.0.1:3002 external-server (verifies the HMAC signature)
   |
   v
Panelica panel + the Linux services it manages

Is it safe to let an AI agent manage my server?​

It is exactly as safe as the API key you hand it, which is the point of scoping:

  • Nothing is preselected. Creating an API key in the panel starts with a blank scope list and a live search box over 50 scopes — you decide exactly what this assistant can touch.
  • HMAC-SHA256 signed requests. Every call is signed over METHOD + PATH + QUERY + TIMESTAMP + BODY with your secret. The panel rejects anything whose timestamp drifts more than 5 minutes, so replays don't work.
  • The secret never leaves your machine. It's read from the process environment, used only to compute the signature, never logged and never written to disk.
  • RBAC on top of scopes. The key inherits its owner's role. A USER-role key only ever sees that user's own domains, databases, mail, etc. Some routes are ROOT/ADMIN only regardless of scope (access logs, for example).
  • Full audit trail. Every call the assistant makes hits the panel's normal audit log and is indistinguishable from any other authenticated API call — traceable to the exact key that made it.
  • Rate limited. Keys carry a tier (60 to unlimited requests/minute — table below), so a runaway loop can't hammer the panel.
  • No data harvesting. No telemetry, no cache, no third party ever sees your traffic.

The practical advice: start read-only, add write scopes only for the task at hand, and reserve destructive scopes (
Code:
*:delete
,
Code:
accounts:delete
,
Code:
domains:delete
,
Code:
backups:restore
) for sessions you're actively watching.

What do I need before I start?​

  • A running Panelica panel — version 1.0.193 or newer recommended; the External API surface is stable from 1.0.180+.
  • HTTPS access to the panel UI on port 8443 from whichever machine will run
    Code:
    panelica-mcp
    — the same port you already use in a browser, no extra firewall change needed.
  • Either Node.js ≥ 20 (for the npm path) or Docker (for the container path) on that machine.

You do not need to install anything on the panel host itself, and you do not need to open the internal port 3002 to the public internet.

How do I generate an API key?​

  1. Sign in to the panel as root or any account with permission to manage API keys.
  2. Go to Settings → API Keys → Generate API Key.
  3. Pick the scopes the assistant needs from the live-search list. For a read-only assistant,
    Code:
    *:read
    is enough on panel 1.0.528+ (older panels need the individual
    Code:
    <area>:read
    scopes). For full automation, grant
    Code:
    *:write
    and
    Code:
    *:delete
    too, or
    Code:
    *:*
    . Every tool's description in this server states the scopes it requires.
  4. Copy both the key (
    Code:
    pk_...
    ) and the secret (
    Code:
    sk_...
    ). The secret is shown only once — put it in a password manager, not a chat log.

Before wiring up a client, prove the credentials work end to end:

Bash:
export PANELICA_BASE_URL=https://your-panel-host:8443/api/external
export PANELICA_API_KEY=pk_xxxxxxxx
export PANELICA_API_SECRET=sk_xxxxxxxx

TS=$(date +%s)
# Signature is over METHOD + PATH + TIMESTAMP + BODY. The path is the
# backend-visible path (/v1/...), NOT the /api/external/ prefix nginx
# strips before forwarding — panelica-mcp does this automatically.
SIG=$(printf "GET/v1/api-keys${TS}" \
  | openssl dgst -sha256 -hmac "$PANELICA_API_SECRET" -hex | awk '{print $2}')

curl -sk "$PANELICA_BASE_URL/v1/api-keys" \
  -H "X-API-Key:   $PANELICA_API_KEY" \
  -H "X-Timestamp: $TS" \
  -H "X-Signature: $SIG"

You should get back JSON listing your API keys. If you get a 401 instead, jump to Troubleshooting below.

Which PANELICA_BASE_URL should I use?​

ScenarioRecommended PANELICA_BASE_URL
MCP client on your laptop, panel on a remote server
Code:
https://<panel-host>:8443/api/external
MCP client and panel on the same machine
Code:
http://127.0.0.1:3002

Do not open port 3002 to the public internet — it should be reached only through the 8443 reverse proxy or from
Code:
127.0.0.1
. If TLS verification fails, that is almost always the panel's self-signed certificate; install a real one from the panel UI (Settings → SSL) rather than disabling verification client-side.

How do I connect Claude Code?​

One command:

Bash:
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

Ask it to "list my domains" and watch it pick up the tool.

How do I connect Claude Desktop?​

Edit the config file:

  • macOS:
    Code:
    ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows:
    Code:
    %APPDATA%\Claude\claude_desktop_config.json
  • Linux (beta):
    Code:
    ~/.config/Claude/claude_desktop_config.json

JSON:
{
  "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, fully quit Claude Desktop (not just close the window) and reopen it. A new chat shows
Code:
panelica
as a connected server with 37 tools available (the core set plus 3 meta tools — set
Code:
PANELICA_TOOLSETS=all
for all 404).

How do I connect Cursor?​

Settings → MCP → Add new server:

JSON:
{
  "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 caps active tools at roughly 40 across all connected MCP servers and silently drops the rest — this is exactly why the default toolset here is a 37-tool core set instead of dumping all 404 tools on every client.

How do I connect OpenAI's Codex CLI?​

Add to
Code:
~/.codex/config.toml
:

Code:
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_..."

What about Continue.dev, Cline, Zed, Docker, or a generic client?​

Any MCP-aware editor that accepts a stdio command works the same way — give it
Code:
npx -y panelica-mcp
(or the absolute path to a built
Code:
dist/index.js
) plus the same three environment variables. Google's Gemini CLI and other MCP-capable tools connect the same generic way; this guide documents Claude Code, Claude Desktop, Cursor and Codex CLI specifically because they have their own config file formats worth spelling out.

Docker, if you'd rather not touch Node.js on the host:

Bash:
docker pull ghcr.io/panelica/panelica-mcp:latest

JSON:
{
  "command": "docker",
  "args": [
    "run", "--rm", "-i",
    "-e", "PANELICA_BASE_URL",
    "-e", "PANELICA_API_KEY",
    "-e", "PANELICA_API_SECRET",
    "ghcr.io/panelica/panelica-mcp:latest"
  ],
  "env": {
    "PANELICA_BASE_URL": "https://your-panel-host:8443/api/external",
    "PANELICA_API_KEY":  "pk_...",
    "PANELICA_API_SECRET": "sk_..."
  }
}

Code:
-i
keeps stdin attached so the client can talk to the container;
Code:
--rm
cleans it up on disconnect. The image runs as a non-root user and exposes no ports — it only speaks stdio.

Building from source, if you'd rather read the code first:

Bash:
git clone https://github.com/Panelica/panelica-mcp.git
cd panelica-mcp
npm install
npm run build
node dist/index.js

Full source and issue tracker: https://github.com/Panelica/panelica-mcp. Package: https://www.npmjs.com/package/panelica-mcp. It's also listed in the official MCP Registry as
Code:
io.github.Panelica/panelica-mcp
, so any client that discovers servers from there finds it automatically.

Which tools does the assistant see by default?​

MCP clients budget tools — Cursor's ~40-tool cap is the tightest, and every registered tool costs prompt tokens on every turn. So by default the server registers a compact core set: 34 everyday tools (accounts, domains, DNS, SSL, databases, e-mail, FTP, backups, server status/services, WordPress, Docker, plans) plus 3 meta tools that reach the whole catalogue.

ToolWhat it does
Code:
panelica_find_tools
Keyword search over all 404 tools; understands plain-language terms — website→domain, certificate→ssl, mailbox→email, container→docker — and stems plurals
Code:
panelica_describe_tool
One tool in full: every parameter with its provenance, body fields (type, required, enum, default), and the response envelope
Code:
panelica_call
Runs any catalogue tool by name with its arguments, through the same scoped, HMAC-signed client

Choose the set with
Code:
PANELICA_TOOLSETS
(comma-separated, unioned):

ValueRegistered tools
Code:
core
(default)
34 + 3 meta = 37
Code:
all
Every catalogue tool + 3 meta (all 404 — fine for Claude Code / Claude Desktop, which don't have Cursor's cap)
Code:
none
Only the 3 meta tools
Category slugs, e.g.
Code:
domains,dns,ssl,git,docker,file_manager,laravel_apps,node_js_apps,python_apps,logs
Just those categories (plus
Code:
core
if listed)

A direct call to a tool that isn't registered still works — a client that learned the name from
Code:
panelica_find_tools
can call it either way.

What does the assistant know beyond the tool list?​

An assistant that only sees 400 tool names guesses ids, retries 403s, and misreads responses. This server hands it the API "by heart" instead:

  • Your key, up front. At startup the server calls
    Code:
    GET /v1/me
    once and tells the model which key it holds, its scopes, tier and expiry — a read-only key is announced as read-only, and out-of-scope tools are declined with the scope to add instead of being attempted. A rejected credential is reported before the first tool call. (
    Code:
    PANELICA_STARTUP_PROBE=0
    disables this.)
  • Scopes as the code enforces them. Newer panels derive required scopes from the middleware chain rather than hand-written docs — including "one of A or B" rules and ROOT/ADMIN role limits — and the tool description repeats them exactly.
  • Allowed values, not guesses. Fields with a fixed value list —
    Code:
    ssl_provider
    ,
    Code:
    web_server
    , database user
    Code:
    role
    , DNS record
    Code:
    type
    , account
    Code:
    role
    — carry a JSON-schema enum and default.
  • Big lists stay usable. Every list-returning GET accepts
    Code:
    _limit
    ,
    Code:
    _fields
    (comma list) and
    Code:
    _match
    (case-insensitive substring); the server applies them and reports
    Code:
    {total, matched, shown}
    , so a panel with hundreds of domains can be searched in one call.
  • Validation errors name the field. A Gin validation failure becomes
    Code:
    field "user_id" is required
    , using the JSON field name the model already used.
  • Search speaks your language.
    Code:
    panelica_find_tools
    maps website→domain, certificate→ssl, mailbox→email, container→docker, and stems plurals.
  • Transient limits are absorbed. A 429 whose window resets within 15 seconds is waited out once automatically; a GET that fails on the network is retried once; a clock skew above two minutes against the panel is flagged (HMAC timestamps are rejected beyond ±5 minutes).
  • Every tool description carries the HTTP route, required scopes, a
    Code:
    Returns:
    line with response fields, and a risk class — read-only, mutating, or destructive.
  • Id parameters say where they come from — "UUID of the domain — obtain it from GET /v1/domains" — for path, query and body fields alike.
  • Errors are explained, not echoed. A 403 names the missing scope and says retrying won't help; a 404 says to re-list and use a real id; a 429 reports the reset window; a 5xx says to report it, not loop.
  • Results are bounded. Oversized list responses are cut to the first items with an explicit
    Code:
    _truncated
    note (still valid JSON) instead of flooding the model's context (
    Code:
    PANELICA_MAX_RESULT_CHARS
    , default 60000).
  • MCP safety annotations.
    Code:
    readOnlyHint
    on GETs and
    Code:
    destructiveHint
    on DELETEs — capable clients like Cursor and Codex use these to auto-approve reads and ask before writes/deletes.
  • Resources for clients that support them:
    Code:
    panelica://guide
    (the instructions),
    Code:
    panelica://catalogue
    (every tool with route and summary), and in live mode
    Code:
    panelica://spec
    (your panel's full API spec).

What can I actually ask it to do?​

Real prompts that work today, grouped by job, with the tool(s)/scope(s) behind each:

Accounts (
Code:
accounts:read
/
Code:
accounts:write
)
  • "List every account on this panel and which plan each is on." —
    Code:
    panelica_accounts_get_v1_accounts
  • "Create a new account for [email protected] on the professional plan." —
    Code:
    panelica_accounts_post_v1_accounts
  • "Suspend the account for [email protected]." —
    Code:
    panelica_accounts_post_v1_accounts_id_suspend

Domains (
Code:
domains:read
/
Code:
domains:write
)
  • "Which domains exist on this panel and what PHP version does each use?" —
    Code:
    panelica_domains_get_v1_domains
  • "Add shop.example.com to account X and set it to PHP 8.3." —
    Code:
    panelica_domains_post_v1_domains
    then the domain's PHP PATCH tool
  • "List the subdomains under shop.example.com." — the domain's subdomains GET tool

DNS (
Code:
dns:read
/
Code:
dns:write
/
Code:
dns:delete
)
  • "List the DNS records for shop.example.com and tell me whether it has an SPF record." — the zone records GET tool
  • "Add a CNAME www → shop.example.com and an A record mail → 203.0.113.10." — the zone records POST tool
  • "Create a TXT record _mcp-test with content mcp-ok on shop.example.com, verify it exists, then delete it." — POST the record, GET the zone to verify, DELETE the record

SSL (
Code:
ssl:read
/
Code:
ssl:write
)
  • "Show the SSL certificate status for shop.example.com: issuer, expiry, auto-renew." — the SSL status GET tool
  • "List every domain whose certificate expires in the next 14 days and renew them all." — SSL status GET, then the issue POST tool per match
  • "Which values are allowed for ssl_provider and web_server on this panel?" — answered straight from the schema without a call (letsencrypt / self_signed / none default letsencrypt; nginx_apache / nginx_only default nginx_apache)

Email (
Code:
email:read
/
Code:
email:write
)
  • "How many email accounts exist across all domains?" —
    Code:
    panelica_email_get_v1_email-accounts
  • "Create a mailbox [email protected]." —
    Code:
    panelica_email_post_v1_email-accounts

Databases (
Code:
databases:read
/
Code:
databases:write
)
  • "How many databases exist and which is the largest?" —
    Code:
    panelica_databases_get_v1_databases
  • "Create a MySQL database for shop.example.com with a dedicated user." —
    Code:
    panelica_databases_post_v1_databases

Backups (
Code:
backups:read
/
Code:
backups:write
)
  • "Show the backup schedule and the last successful backup for account X." — the backups GET tool
  • "Kick off a full backup for shop.example.com right now." — the backups POST tool

WordPress
  • "Which WordPress sites are installed, and are any plugin or core updates pending?" —
    Code:
    panelica_wordpress_get_v1_wordpress
  • "Update WordPress core on shop.example.com." —
    Code:
    panelica_wordpress_post_v1_wordpress_id_update-core
Run
Code:
panelica_describe_tool
on either name first — the exact scope requirement your panel enforces is printed in the tool's own description rather than guessed here.

Docker (
Code:
docker:read
)
  • "List every running container and how much memory each is using." —
    Code:
    panelica_docker_get_v1_docker_containers
  • "Which containers belong to account X?" — same tool, filtered

Server health (
Code:
server:read
/
Code:
services:restart
)
  • "Give me a short server health summary: status, running services, load and memory." —
    Code:
    panelica_server_get_v1_server_status
    ,
    Code:
    _services
    ,
    Code:
    _metrics
  • "Restart the mysql service." —
    Code:
    panelica_server_post_v1_server_services_name_restart

The assistant only ever calls tools whose scopes your key actually has — a read-only key safely answers every "list" and "show" question here and declines every "create" / "delete" one, naming the scope it's missing.

What does a real session look like?​

We ran OpenAI's Codex CLI 0.154 against a dev panel through this server: 14 tasks across two rounds, zero invented ids — every id it used came from a list call first. Before any write it called
Code:
panelica_describe_tool
to check exact parameters rather than guessing. Given a key scoped to read-only plus
Code:
dns
, asked to create a domain, it declined up front and named the missing scope (
Code:
domains:write
) instead of attempting the call. Asked to manage a TXT record, it created
Code:
_mcp-test
with content
Code:
mcp-ok
, verified the record existed by re-listing the zone, deleted it, and confirmed the deletion — list → id → act → verify, the exact pattern the server's instructions are built around. It used
Code:
_fields
,
Code:
_match
and
Code:
_limit
on list calls without being asked to, to keep large responses small. Asked which values are allowed for
Code:
ssl_provider
and
Code:
web_server
, it answered straight from the JSON schema instead of guessing. We are not publishing timings or token costs from this session — the point here is the behaviour, not the speed.

A full transcript of a similar session with Claude Code — creating a domain and issuing an SSL certificate end to end — is posted in the "Show & Tell" thread linked below.

What scopes exist and what do they control?​

No scope is preselected when you create a key — the create dialog has live search over all 50. Every family also accepts its wildcard (e.g.
Code:
domains:*
), and
Code:
*:*
grants everything.

AreaScopes
Accounts
Code:
accounts:read
·
Code:
accounts:write
·
Code:
accounts:delete
Domains & subdomains
Code:
domains:read
·
Code:
domains:write
·
Code:
domains:delete
Databases
Code:
databases:read
·
Code:
databases:write
·
Code:
databases:delete
DNS
Code:
dns:read
·
Code:
dns:write
·
Code:
dns:delete
Email
Code:
email:read
·
Code:
email:write
·
Code:
email:delete
FTP
Code:
ftp:read
·
Code:
ftp:write
·
Code:
ftp:delete
SSL
Code:
ssl:read
·
Code:
ssl:write
Backups & snapshots
Code:
backups:read
·
Code:
backups:write
·
Code:
backups:restore
File Manager
Code:
files:read
·
Code:
files:write
·
Code:
files:delete
CloudFlare
Code:
cloudflare:read
·
Code:
cloudflare:write
·
Code:
cloudflare:delete
Docker & app templates
Code:
docker:read
·
Code:
docker:write
·
Code:
docker:delete
App hosting (Laravel / Node.js / Python)
Code:
apps:read
·
Code:
apps:write
·
Code:
apps:delete
Git & Deploy
Code:
git:read
·
Code:
git:write
·
Code:
git:delete
Logs & audit
Code:
logs:read
·
Code:
logs:write
Security (antivirus, firewall, IP blocks)
Code:
security:read
·
Code:
security:write
·
Code:
security:delete
Server & infrastructure
Code:
server:read
·
Code:
server:write
Service control
Code:
services:restart
·
Code:
services:start
·
Code:
services:stop
Plans
Code:
plans:read
·
Code:
plans:write
Webhooks
Code:
webhooks:read
·
Code:
webhooks:write
·
Code:
webhooks:delete
Bandwidth
Code:
bandwidth:read
License
Code:
license:read
Migrations (panel-to-panel)
Code:
migrations:read
Terminal
Code:
terminal:access
Full access
Code:
*:*

Mutating service control deliberately requires its own action scopes (or
Code:
server:write
) — a metrics-only
Code:
server:read
key cannot stop MySQL.

How many requests can it make per minute?​

API keys carry a rate-limit tier chosen when you create the key:

TierRequests / minute
starter (default)60
professional300
business1000
enterpriseunlimited

Responses carry
Code:
X-RateLimit-Remaining-Minute
and
Code:
X-RateLimit-Reset-Minute
headers. The default
Code:
starter
tier is easy to exhaust when an assistant fans out a lot of calls in a row — if you see
Code:
429 RATE_LIMIT_EXCEEDED
a lot, create the assistant's key with a higher tier. A reset window of 15 seconds or less is waited out once automatically by the server itself.

How is this secured, in detail?​

  • HMAC-SHA256 request signing over METHOD + PATH + QUERY + TIMESTAMP + BODY.
  • Secrets stay local — read from the environment, used only for signing, never logged or written to disk.
  • Scope-restricted keys — one key per use case, only the scopes that use case needs.
  • Full audit trail — every call is a normal authenticated API call in the panel's audit log, traceable to the key.
  • No data harvesting — no telemetry, no cache, no third party sees your traffic.
  • Container hardening — the Docker image runs as a non-root user and exposes no ports; it only speaks stdio.
  • SSRF prevention — private/reserved IPs are rejected for A/AAAA DNS records.
  • FTP accounts are fenced to the owning account's home directory; passwords must be at least 8 characters.
  • Keys can be IP-whitelisted and given an expiry (
    Code:
    expires_in
    days) at creation, and revoked instantly from the panel at any time.

Something isn't working — troubleshooting​

SymptomLikely causeFix
Client reports "0 tools available"Server crashed at startup — usually a missing env varRun
Code:
panelica-mcp
once from a shell with the three env vars set and read stderr
Code:
401 MISSING_API_KEY
Code:
PANELICA_API_KEY
not set or wrong header passthrough
Re-check the client config; restart the client after editing
Code:
401 INVALID_SIGNATURE
Wrong
Code:
PANELICA_API_SECRET
, or clock drift > 5 min
Check NTP sync on both the MCP host and the panel host
Code:
401 INVALID_TIMESTAMP
Local clock drift > 5 minSync NTP on the MCP host
Connect timeout on BASE_URLWrong host/port — usually
Code:
:8443/api/external
was left off
Code:
curl -sk $PANELICA_BASE_URL/health
should return
Code:
{"status":"ok"}
Tool result starts with "Panelica API error 403" and names a scopeYour key lacks that scopeAdd it in Settings → API Keys — the assistant is told not to retry
Tool result says "404 … re-list"The assistant used an id that doesn't exist for this key's ownerNothing to fix server-side — it will re-list and pick a real id
"Schema not statically declared"The endpoint binds a dynamic body (map/multipart)Pass a free-form
Code:
body
object; the panel's 400 response names the missing field
Startup log: "clock skew of Ns versus the panel"The MCP host's clock is offSync NTP; signed requests carry a timestamp the panel checks
Startup log: "startup credential check failed"Key/secret rejected by
Code:
GET /v1/me
Fix the key in the panel and restart the client
TLS verification failsPanel is using its self-signed certificateInstall a real cert (panel UI → Settings → SSL) — don't disable verification client-side

Still stuck? Open an issue at https://github.com/Panelica/panelica-mcp/issues with the (redacted) stderr output, or post here.

Frequently asked questions​

Do I need to change anything on the panel itself? No. Only an API key with the scopes you choose. The External API has been part of every Panelica panel since 1.0.180.

Will new panel features show up automatically? Yes — in live mode (the default) the server pulls your panel's own
Code:
/v1/api-spec
at startup, so a new endpoint on your panel becomes a new tool the next time the client reconnects, with no update to
Code:
panelica-mcp
required.

Can I point one MCP server config at more than one panel? No — each
Code:
panelica-mcp
process is scoped to one
Code:
PANELICA_BASE_URL
and one key pair. Add a second MCP server entry with its own env vars if you manage more than one panel.

Does it work with anything besides Claude, Cursor and Codex CLI? Any MCP-compatible client works the same way — three env vars and a stdio command. This guide covers Claude Code, Claude Desktop, Cursor and Codex CLI specifically because their config formats differ; Continue.dev, Cline, Zed, Gemini CLI and others connect identically.

What happens if I don't set PANELICA_TOOLSETS? It defaults to
Code:
core
: 34 everyday tools plus the 3 meta tools, 37 total.

Can the assistant see other accounts' data? No. The key inherits its owner's role and the panel's RBAC — a USER-role key only ever sees that user's own domains, databases, mail and files.

Does it cache or store anything? No — stateless adapter, no telemetry, nothing written to disk.

My panel uses a self-signed certificate — do I disable TLS verification client-side? Don't. Install a real certificate on the panel (Settings → SSL) instead.

How do I know exactly which scope a specific tool needs? Ask the assistant to run
Code:
panelica_describe_tool
on it, or read the tool's own description — every one states its required scopes, response fields and risk class.

What's the difference between calling a tool directly and using panelica_call? They hit the same scoped, signed HTTP client —
Code:
panelica_call
is there so a client that only has the 37 core tools registered can still reach any of the other 367 by name, once it has found them with
Code:
panelica_find_tools
.

Where to go next​

  • GitHub: https://github.com/Panelica/panelica-mcp
  • npm: https://www.npmjs.com/package/panelica-mcp
  • Docker image:
    Code:
    ghcr.io/panelica/panelica-mcp
  • MCP Registry entry:
    Code:
    io.github.Panelica/panelica-mcp
  • Announcement: see "Panelica MCP Server 0.5.1: connect Claude, Cursor, Codex and Gemini to your panel" in Announcements
  • Quick start: "How to: Connect Claude Code to your Panelica server in 60 seconds" in this section
  • Real transcript: "Show & Tell: I asked Claude to create a site and issue SSL — full transcript"
  • Scope cheat-sheet: "Which permissions should you give an AI assistant?"
  • For the panel's own built-in AI desktop (runs inside the panel instead of on your machine), see the OpsAI guide in General Discussion.

Questions, edge cases, and "it did something unexpected" reports are all welcome below.
 
Back
Top