Refine agent skill boundaries and secret handling

This commit is contained in:
jxxghp
2026-06-21 09:25:44 +08:00
parent 18803c7995
commit 99e369aaa4
12 changed files with 783 additions and 794 deletions
+41 -10
View File
@@ -1,7 +1,14 @@
---
name: moviepilot-api
version: 1
description: Use this skill when you need to call MoviePilot REST API endpoints directly. Covers all 245 API endpoints across 27 categories including media search, downloads, subscriptions, library management, site management, system administration, plugins, workflows, and more. Use this skill whenever the user asks to interact with MoviePilot via its HTTP API, or when the moviepilot-cli skill cannot cover a specific operation.
version: 2
description: >-
Use this skill when you need to call MoviePilot REST API endpoints directly
with the bundled Python client. Covers MoviePilot HTTP endpoints across media
search, downloads, subscriptions, library management, site management, system
administration, plugins, workflows, and more. Prefer `moviepilot-cli` for
normal local MCP tool workflows; use this skill when the user explicitly asks
for HTTP API access, when an endpoint is not exposed as an MCP tool, or when
running in an environment where direct REST calls are the appropriate bridge.
---
# MoviePilot REST API
@@ -10,15 +17,38 @@ description: Use this skill when you need to call MoviePilot REST API endpoints
Use `scripts/mp-api.py` to call any MoviePilot REST API endpoint directly.
## Scope And Boundaries
This skill is the REST API bridge. It is implemented as a Python script and is
useful when the agent needs endpoint-level coverage beyond the local
`moviepilot tool` MCP CLI.
Choose other skills first when they match more precisely:
| Request | Preferred skill |
|---|---|
| Normal local MoviePilot product operation exposed as an MCP tool | `moviepilot-cli` |
| Direct SQL query or database update | `database-operation` |
| Restart, version check, or upgrade | `moviepilot-update` |
| Slash commands or plugin/system command dispatch | `command-dispatch` |
| Browser-only state, site login pages, screenshots, cookies | `browser-use` |
Do not use this skill just because MoviePilot is mentioned. Use it when the
task specifically needs a REST endpoint, token-query endpoint, or API behavior
that the CLI/MCP tools do not expose.
## Setup
Configure the backend host and API key (persisted to `~/.config/moviepilot_api/config`):
When the script runs inside the MoviePilot project, it imports `app.core.config.settings` and reads `settings.HOST`, `settings.PORT`, and `settings.API_TOKEN` directly. Do not ask the user for `API_TOKEN`, and do not copy API keys into the prompt.
```
python scripts/mp-api.py configure --host http://localhost:3000 --apikey <API_TOKEN>
```
Configuration priority:
The API key is the `API_TOKEN` value from MoviePilot settings.
1. CLI flags: `--host`, `--apikey`
2. Environment variables: `MP_HOST`, `MP_API_KEY`
3. Local MoviePilot settings
4. Legacy config file: `~/.config/moviepilot_api/config`
Use `configure` only as a legacy fallback outside the MoviePilot project, and avoid it in normal agent workflows because it persists a long-lived API key to disk.
## How to Call APIs
@@ -30,9 +60,10 @@ python scripts/mp-api.py <METHOD> <PATH> [key=value ...] [--json '<body>']
### Authentication
- By default, the key is sent via the `X-API-KEY` header.
- By default, the script auto-loads the local key and sends it via the `X-API-KEY` header.
- For endpoints suffixed with `2` (e.g. `/api/v1/dashboard/statistic2`), use `--token-param` to send the key as `?token=`.
- Both methods validate against the same `API_TOKEN` value.
- Never print, summarize, or ask the user to paste the API key unless the script is being used outside the local project and no safer configuration source is available.
### Examples
@@ -564,9 +595,9 @@ python scripts/mp-api.py GET /api/v1/site/cookiecloud
| Scenario | Action |
|----------|--------|
| HTTP 401 | API key is invalid or missing. Re-run `configure` with correct `--apikey`. |
| HTTP 401 | API key is invalid or missing. Verify local settings with `moviepilot doctor`; only use `--apikey` as an external fallback. |
| HTTP 403 | Insufficient permissions. The API key grants superuser access; check if the endpoint requires special auth. |
| HTTP 404 | Endpoint or resource not found. Verify the path and path parameters. |
| HTTP 422 | Validation error. Check required parameters and JSON body format. |
| Connection error | Verify `--host` URL is reachable. Check if MoviePilot is running. |
| Missing config | Run `python scripts/mp-api.py configure --host <HOST> --apikey <KEY>` first. |
| Missing config | Run inside the MoviePilot project, or set `MP_HOST` and `MP_API_KEY` in the process environment. |
+47 -12
View File
@@ -14,7 +14,7 @@ Authentication:
It can also fall back to ``?token=`` for endpoints that require it.
Configuration priority:
CLI flags > Environment variables (MP_HOST / MP_API_KEY) > Config file
CLI flags > Environment variables > local MoviePilot settings > Config file
Config file location: ~/.config/moviepilot_api/config
"""
@@ -23,17 +23,20 @@ from __future__ import annotations
import json
import os
import sys
import ssl
import stat
import urllib.request
import sys
import urllib.error
import urllib.parse
import ssl
import urllib.request
from pathlib import Path
SCRIPT_NAME = os.path.basename(sys.argv[0]) if sys.argv else "mp-api.py"
SCRIPT_PATH = Path(__file__).resolve()
PROJECT_ROOT = SCRIPT_PATH.parents[3]
CONFIG_DIR = Path.home() / ".config" / "moviepilot_api"
CONFIG_FILE = CONFIG_DIR / "config"
LOCAL_HOSTS = {"0.0.0.0", "::", "::1", "", "localhost"}
# ---------------------------------------------------------------------------
# Configuration helpers
@@ -63,19 +66,53 @@ def read_config() -> tuple[str, str]:
def save_config(host: str, apikey: str) -> None:
"""Persist host and API key to the legacy config file."""
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
CONFIG_FILE.write_text(f"MP_HOST={host}\nMP_API_KEY={apikey}\n", encoding="utf-8")
CONFIG_FILE.chmod(stat.S_IRUSR | stat.S_IWUSR)
def _ensure_project_import() -> None:
"""Add the MoviePilot project root to sys.path for local auto-configuration."""
project_path = str(PROJECT_ROOT)
if project_path not in sys.path:
sys.path.insert(0, project_path)
def _client_host(host: str) -> str:
"""Return a loopback host usable by local clients."""
host = (host or "").strip()
if host in LOCAL_HOSTS:
return "127.0.0.1"
return host
def read_local_config() -> tuple[str, str]:
"""Return host and key from local MoviePilot settings when available."""
try:
_ensure_project_import()
from app.core.config import settings # pylint: disable=import-outside-toplevel
except Exception:
return "", ""
host = str(settings.HOST or "")
port = settings.PORT
apikey = str(settings.API_TOKEN or "")
if host and port:
return f"http://{_client_host(host)}:{port}", apikey
return "", apikey
def resolve_config(
cli_host: str = "",
cli_key: str = "",
) -> tuple[str, str]:
"""Resolve effective host & key using priority: CLI > env > file."""
"""Resolve effective host and key without requiring prompt-visible secrets."""
local_host, local_key = read_local_config()
cfg_host, cfg_key = read_config()
host = cli_host or os.environ.get("MP_HOST", "") or cfg_host
apikey = cli_key or os.environ.get("MP_API_KEY", "") or cfg_key
host = cli_host or os.environ.get("MP_HOST", "") or local_host or cfg_host
apikey = cli_key or os.environ.get("MP_API_KEY", "") or local_key or cfg_key
return host, apikey
@@ -199,11 +236,11 @@ def print_json(obj: object) -> None:
def print_usage() -> None:
print(f"""Usage: python {SCRIPT_NAME} [options] <METHOD> <PATH> [key=value ...] [--json '<body>']
python {SCRIPT_NAME} configure --host <HOST> --apikey <KEY>
python {SCRIPT_NAME} configure --host <HOST> --apikey <KEY> # legacy fallback
Options:
--host HOST MoviePilot backend URL
--apikey KEY API key (API_TOKEN)
--host HOST MoviePilot backend URL (auto-read locally when omitted)
--apikey KEY API key (auto-read locally when omitted)
--token-param Send key as ?token= query param instead of X-API-KEY header
--timeout SECS Request timeout (default: 120)
--help Show this help message
@@ -211,8 +248,6 @@ Options:
Methods: GET POST PUT DELETE
Examples:
python {SCRIPT_NAME} configure --host http://localhost:3000 --apikey mytoken123
python {SCRIPT_NAME} GET /api/v1/media/search title="Avatar" type="movie"
python {SCRIPT_NAME} GET /api/v1/subscribe/
python {SCRIPT_NAME} POST /api/v1/download/add --json '{{"torrent_url":"abc:1"}}'