# MoviePilot MCP 接入说明 本文面向通过 MCP 调用 MoviePilot 的外部 Agent 和工具开发者。接入后,客户端通过工具目录发现能力,按 JSON Schema 构造参数,并根据业务结果判断操作是否完成。 **最短调用路径:**配置连接与密钥 → `initialize` → `notifications/initialized` → `tools/list` → `tools/call`。已有 MCP 客户端通常会自动完成初始化和工具发现,只需配置下表中的连接信息。 ### 插件安装前说明入口 插件目录的可选 `plugin_readme` 提供 HTTP(S) 文档外链,兼容索引中的 `homepage` 和 `project_url`。 详情界面通过管理员接口 `GET /api/v1/plugin/readme/{plugin_id}?repo_url=...` 查询当前来源, 返回 `{"url":"https://..."}`;链接未声明时按所选候选的实际代际探测 GitHub README, 缺失或超时返回 `{"url":null}`,不要求先安装插件。本地来源只返回显式声明的链接。 探测总时限为五秒,结果缓存五分钟;接口不返回文档正文,也不新增 Agent operation。 ### 订阅下载的提交来源 新产生的订阅下载在订阅活动和日志中区分“首次订阅”“洗版升级”或“全集洗版”。 下载通知的标题和正文按通知模板渲染,不额外拼接来源标签。 该标记来自提交前订阅的已下载事实与最终选集,不改变过滤、优先级、选集和下载策略。 同一整包包含已下载集与缺集时计作一次洗版任务,分别保留 `initial_episodes` 和 `upgrade_episodes`;`best_version_full` 表示此次提交使用的全集洗版模式。 - `GET /api/v1/subscribe/{id}/activity` 的 `counts` 保留 `submitted` 总量,增加 `initial_submitted`、`upgrade_submitted`、`full_upgrade_submitted`;最后一项是洗版计数的子集。 - 活动详情 `details.resources[].subscription_download` 保存 `subscription_id`、 `submission_type`(`initial` / `upgrade`)、`best_version_full` 及两类集数。 活动可从订阅历史继续查看,恢复重试按资源去重,不重复累计成功回执。 - `GET /api/v1/subscribe/execution/batches` 及其 `/{batch_id}` 详情返回 `download_counts`、 `downloads` 和 `downloads_omitted`。分类数量按成功下载任务计数,与搜索任务完成数量独立; 明细最多 100 条,裁剪不影响总量,访问权限沿用整批订阅归属校验。 - 下载历史的 `note.subscription_download` 保留同一来源,`note.source` 继续保留原入口信息。 下载处理上下文在持久事件恢复后保留该来源,已冻结的通知保持模板渲染结果。 升级前历史没有来源标记,界面显示“未记录”,不会按当前订阅设置追溯推断。 普通订阅的首次提交与洗版订阅补缺在活动中都标记为首次订阅。 ### 大集合查询窗口 `POST /api/v1/media/status` 接受 1 至 50 个媒体身份/标题/年份/季号查询,按输入顺序返回 `item_id`、`subscribed` 与同来源剧集的各季订阅模式 `seasons`。查询仅访问本地媒体库和 当前用户可见订阅,不调用外部媒体服务器,不返回完整订阅表。普通用户仍受 owner 限制, 影视保留标题、年份回退,音乐按实体类型隔离。该界面专用批量端点不新增 Agent 操作。 `search.recommend` 的 `check_only=true` 只读当前状态,不读取全量搜索结果缓存; 推荐禁用时也直接返回 `disabled`。`force=true` 保留优先级,确实启动任务时才读取候选。 前端轮询等待上次请求结束后再间隔五秒,后台标签暂停发包,离开资源页后停止轮询。 卡片搜索配置与 `system/env` 在前端共享最多三十秒的只读快照,并合并并发请求; 各表单持有独立副本。相关站点、系统设置和订阅写操作成功后,以及登录身份变化后, 快照立即失效,在途旧结果不能写回新会话;失败查询允许重试。 `GET /api/v1/site/userdata/{site_id}` 支持 `page` / `count`;趋势界面使用最近 90 条记录, 并传入 `latest_seeding_only=true`,仅窗口内最新记录携带 `seeding_info`,历史统计字段不变。 省略该选项时保留每条记录的完整明细;这不会删除或修改采集历史。 `GET /api/v1/site/category/{site_id}` 只返回分类项,不包含 `field`、`delimiter`、`param` 等搜索配置。分类 `id` 支持整数和文本,数字字符串沿用整数响应;客户端应保留返回值, 浏览资源时将所选分类用逗号拼接后传入 `site.resource` 的 `query.cat`,不要强制转为整数。 `GET /api/v1/torrent/cache` 支持可选 `page` / `count`,每页最多 200 条,省略两项时保留全量合同。 `title` 为不区分大小写的标题包含条件,`site` 为站点名称精确条件,均在分页前应用。 `brief=true` 将描述截至 200 字符,缓存 hash 仍由完整原文生成。 返回的 `count` / `sites` 是完整缓存统计,`total` 是筛选后数量,`site_names` 包含完整可选站点。 分页只限制明细构造和响应,不改变底层缓存读取语义。 `GET /api/v1/subscribe/calendar?start=2026-10-01&end=2026-11-01&page=1&count=50` 面向日历界面,按真实播出/上映日期过滤后排序、分页,返回 `items` / `total`。 `start` 包含当天,`end` 不包含当天,跨度最多 93 天,每页最多 200 条。 每项仅含订阅展示/入库摘要、日期、分集标题/集号及片长,不包含窗口外分集、简介和演职人员。 普通用户只能查询自己的订阅,管理员可查询全部;音乐订阅不进入影视日历。 详情查询最多四路并发,复用现有媒体预缓存及五分钟的有界精简缓存;冷缓存仍可能等待媒体源。 翻月或离开页面后客户端取消旧窗口,服务端探测断开后停止继续派发详情。 该界面专用聚合端点未增加 Agent 操作;Agent 的 `subscription.list` 合同不变。 ### App 小组件只读接口 主 App 使用既有登录通过 `/api/v1/widget/context` 核对账号、`/api/v1/widget/registrations` 登记。 扩展使用独立的有限期只读 Bearer 通过 `/api/v1/widget/snapshot` 刷新。 该凭据不适用于普通 REST/MCP,小组件接口也不加入 Agent 工具目录; 详见[授权、权限与快照协议](widgets.md)。主登录凭据不得交给系统扩展。 ### 通知配置与交互身份 `slash.run` 返回的是命令已触发,不代表后台命令或通知已完成。消息渠道发起的命令使用 宿主签名的渠道用户身份;请求头不能指定其他用户,渠道身份缺失或不匹配时会拒绝执行。 Web 会话沿用当前用户;无渠道调用不携带收件人,插件回执按既有管理员通知目标路由, 没有可用目标时不发送。 `GET /api/v1/message/notification` 的可空 `poster` 字段提供独立媒体海报。 新通知从已有媒体上下文提取海报;`image` 继续保留原有渠道图片语义。 旧通知及没有海报的通知返回 `poster: null`,客户端不应将背景图裁切成海报。 `POST /api/v1/notification/config` 保存通知渠道时要求名称和稳定 `id` 各自唯一。 改名应保留原 `id`;复制为新的渠道实例应使用新的 `id`,避免共享登录凭据。 通过消息渠道发起普通重启时,完成回执保留发起时的 `source` 配置。 `POST /api/v1/notification/app` 使用当前激活用户的既有 Bearer 登录态管理 AppPush, 请求包含 `action`(`status/register/unregister/pair/test`)、随机 `installation` 和实例 `origin`。 `register` 另需 APNs `token/topic/environment`;`source` 可选,用于选择启用的 AppPush 渠道。 账号通知身份由后台注入,客户端不能指定其他用户。注销同时撤销该安装配对的主屏幕通知网页。 前端仅在用户主动退出登录时请求 `unregister`;服务重启、认证失效及站点认证完成后的重新登录 只清理登录会话,保留通知绑定。`pair` 返回的绑定码十分钟有效且只能使用一次,完成绑定后无需保留或重新生成。 AppPush 向中心传递独立海报(旧消息回退到图片);通知网页点开后显示本条通知的标题、正文和海报, 并提供 `moviepilot-app://notification` 链接。应先更新 Server,再更新 MoviePilot;图片由接收设备加载,系统通知中的图片展示以浏览器实际支持为准。 正文不再截取前1000个字符;Server 接受最多65536个字符,系统推送只携带预览和随机消息标识。 全文在 Server Redis 按设备绑定版本暂存,每个绑定最多20条、每条24小时;网页用自身绑定能力读取, 不在 URL 中传递正文或令牌。网页也短期暂存最近通知,用于冷启动、恢复和重载时恢复详情,解绑/换绑时清除; 超过暂存期限或读取失败会给出明确提示,永久历史仍在 MoviePilot 通知中心。 网页本地绑定记录缺失时,Server 可校验浏览器现存订阅的完整端点与密钥,恢复原设备能力; 这不需要生成新的绑定码,也不会更改 MoviePilot 登录态或建立另一份订阅。 AppPush 地址 `PUSH_SERVER_URL` 留空时使用 `MP_SERVER_HOST`;首次操作自动生成并先行持久保存256位实例凭据,再向中心幂等登记现有 `user_uid`。UID 只关联来源,不能据此获取、重置或认领已有凭据。内部 `AppPushCredentials` 按中心地址隔离,不进入通知渠道配置;中心只保存凭据摘要。已有 `PUSH_RELAY_KEY` 手工配置继续兼容。 `POST /api/v1/notification/app/instance` 仅容器超级管理员可调用,正文为 `{"action":"rotate","source":"已保存的渠道名称"}`。轮换保留设备,候选凭据先落库,响应丢失可在重启后接续;返回值不包含密钥。普通用户的 `/notification/app` 不接受该动作。 Apple/VAPID 私钥只放在中心 Server,不放入 App 或用户容器。 消息沿用现有类型开关和管理员/用户路由;“所有用户”范围会查询本实例所有启用账号的不可变通知身份, 逐账号投递到各自绑定的设备。停用账号和空身份不会进入接收集合,组合路由中已投递的账号不重复发送。 未经过该范围解析且缺少明确用户目标时不广播,其他渠道继续使用原有默认目标。部署与免费自签说明见 [MoviePilot-Server](https://github.com/jxxghp/MoviePilot-Server#私有-app-消息推送)。 Discord 仅指定 `userid` 的定向通知使用私聊;无法私聊时返回失败,不转发公共频道。 `/skills` 安装和删除按展示时的条目身份操作,刷新或翻页后的旧按钮会失效,应使用最新列表重新选择。 ### 文件整理与恢复 自动选择目的目录时,整理请求应将 `target_path` 留空,由源存储、源目录、媒体类型和分类 匹配启用的存储目录配置;未匹配且没有显式目标路径时,整理会以“未找到有效的媒体库目录”拒绝。 `downloader` 为空不代表使用默认目标路径,也不会自动选择存储根目录。 显式传入 `target_path` 时,若未匹配目录配置,则保留自定义目标路径,并按请求的类型、分类 子目录开关拼接路径;例如 `/media` 可得到 `/media/电影/华语电影`,不会自动补入 `links`。 该分支在预览和实际规划时均输出 WARNING,包含源存储与路径、传入的目标根路径及计算后的目标路径。 需要使用已配置的库路径时,请选择自动匹配,并在执行前通过整理预览核对目标位置。 整理成功历史保存 `episode_group`;批量复用历史时仅恢复所有记录共同的剧集组, 不同剧集组应分别提交。目录整理的叶子文件同样遵守冻结的覆盖策略, `overwrite_skipped` 表示策略拒绝,不能当作文件已导入。 `FileItem` 的可选 `modify_time_ns` 和 `change_time_ns` 保存本地纳秒版本证据, JSON 使用十进制字符串避免浏览器数字精度丢失,客户端应原样保留查询返回值,不自行生成。 恢复清理发现本地源或目标版本变化时会保留副本并转人工复核; 云盘时间戳变化仍按大小与稳定文件标识核验。OpenList 中断或大小不符的下载不会作为移动成功结果删除源文件。 `storage.rename` 的 `recursive=true` 会识别并重命名所有子目录内媒体,随后重命名所选目录。 ## 1. 连接与认证 | 配置项 | 值与含义 | | --- | --- | | 服务地址 | `https:///api/v1/mcp`;使用实际可达的后端地址,反向代理有前缀时需一并保留 | | 传输方式 | Streamable HTTP,向同一个地址 POST JSON-RPC 2.0 消息 | | 认证请求头 | `X-API-KEY: `,值为该 MoviePilot 实例配置的 `API_TOKEN` | | 请求类型 | `Content-Type: application/json` | | 接受类型 | 客户端可发送 `Accept: application/json, text/event-stream`;当前服务端以 JSON 返回调用结果 | | Schema 模式 | 默认 `full`;客户端无法处理大型 Schema 时,增加 `X-MCP-Schema-Mode: compact`,详见[参数合同](#3-获取和使用参数合同) | 客户端必须能够配置远程 HTTP MCP URL 和自定义请求头。不同客户端的配置文件格式不同,将上述值填入其对应配置项即可。这里的服务地址是 HTTP 入口,不是本地进程启动命令。 整理失败的 AI 接管应先读取 `transfer.history` 并核实媒体身份;对持久化任务使用 `transfer.history.replan` 提交 `history_id`、接管时的 `task_id`/`settlement_revision`、`media_source`、`media_id`、`media_type` 以及可选的 `season`/`episode`。该操作只接受没有文件副作用的识别拒绝,保留原任务、失败证据、结算回执和重试预算;排队受理不代表文件已经整理完成。 认证补充: - MCP 使用固定 API 密钥,不使用网页登录的 Bearer JWT,也不提供 OAuth 登录流程。 - 无法设置请求头的客户端可使用 `.../api/v1/mcp?apikey=`。优先使用请求头,避免密钥进入 URL 日志与历史记录。 - 每个请求都需要认证,包括初始化、发现工具和终止会话。密钥校验还要求实例中存在有效的超级管理员账号。 - 这是**管理员级集成入口**,可以访问站点、配置、数据库及有副作用的操作。只向可信客户端提供密钥,并使用 HTTPS 或受信网络。 - 写操作的用户授权由调用方负责取得;不要假设 MCP 会弹出 MoviePilot 内置 Agent 的确认界面。隐藏工具也不构成权限隔离。 REST 兼容集成接口 `GET /api/v1/subscribe/list` 使用 `X-API-KEY: `, 也接受 `?token=`;缺失、错误凭据或绑定的管理员身份失效时返回 HTTP `401`。 认证成功后可读取全部用户的订阅及总量。Agent 的 `subscription.list` 使用当前用户身份访问 `GET /api/v1/subscribe/`,普通用户只能读取自己的订阅。 下文的 HTTP 示例使用两个占位环境变量,由接入方在自己的环境中设置。不要把真实密钥写进文档、模型提示词或代码仓库。 ```bash MP_MCP_URL='https://moviepilot.example.com/api/v1/mcp' MP_API_KEY='<该实例的 API_TOKEN>' ``` ## 2. 完成一次最小调用 ### 2.1 初始化 ```bash curl -sS "$MP_MCP_URL" \ -H "X-API-KEY: $MP_API_KEY" \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"example-agent","version":"1.0.0"}}}' ``` 成功响应位于 `result`,其中包含 `protocolVersion`、`capabilities`、`serverInfo` 和 `instructions`。服务端支持 `2025-11-25`、`2025-06-18`、`2024-11-05`;请求版本不在支持列表时返回默认版本 `2025-11-25`,客户端应检查是否能继续使用该版本。 随后发送初始化完成通知,**不携带 `id`**: ```json { "jsonrpc": "2.0", "method": "notifications/initialized", "params": {} } ``` 使用与初始化相同的地址和认证头 POST。当前实现返回 HTTP `204`,没有 JSON 正文。 ### 2.2 发现工具 ```bash curl -sS "$MP_MCP_URL" \ -H "X-API-KEY: $MP_API_KEY" \ -H 'Content-Type: application/json' \ --data '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' ``` 读取 `result.tools`。每项包含: | 字段 | 客户端用途 | | --- | --- | | `name` | `tools/call.params.name` 必须使用的精确工具名 | | `description` | 工具用途、边界与调用提示 | | `inputSchema` | 本次部署实际提供的 JSON Schema,决定参数名、类型、必填项和可选操作 | 工具目录可能随插件安装、启停或卸载而改变。当前 `capabilities.tools.listChanged=false`,不会主动推送目录变更通知;重连、插件变更或遇到工具不存在时重新发现。不要将本文中的示例当成完整工具清单。 ### 2.3 调用一个只读工具 下面查询第一页订阅,不创建或修改订阅: ```bash curl -sS "$MP_MCP_URL" \ -H "X-API-KEY: $MP_API_KEY" \ -H 'Content-Type: application/json' \ --data '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"moviepilot_api","arguments":{"operation_id":"subscription.list","query":{"page":1,"count":20}}}}' ``` MCP 响应的外层结构如下。`text` 中的内容为示意业务结果: ```json { "jsonrpc": "2.0", "id": 3, "result": { "content": [ { "type": "text", "text": "{\"collection\":{\"result_count\":0,\"total_count\":0,\"page\":1,\"count\":20},\"success\":true,\"message\":\"\",\"data\":[]}" } ] } } ``` `result` 表示协议调用已返回,业务结果在 `content` 的文本块中。文本可解析为 JSON 时再读取其中的 `success`、`error`、`data` 和执行状态;不要仅凭 HTTP `200` 或存在 `result` 判断业务成功。完整处理规则见[结果与恢复](#6-处理结果分页与失败恢复)。 ### 2.4 传输约定 | 方法 | 行为 | | --- | --- | | `POST /api/v1/mcp` | 每次发送一个 JSON-RPC 对象;不支持批量数组请求 | | JSON-RPC `ping` | 返回空对象 `result: {}`,可用于检查协议连通性 | | `DELETE /api/v1/mcp` | 认证后返回 `204`;当前为无状态模式,无需维护服务端 Session ID | 当前没有供旧式 HTTP+SSE 客户端使用的 GET 事件流或独立消息投递地址,也不提供 `resources/list`、`prompts/list`。客户端应选择向同一 URL POST 的 HTTP 传输模式。 ## 3. 获取和使用参数合同 ### 完整模式:按 operation / action 选择分支 默认 `tools/list` 返回完整 Schema。四个主要入口使用 `oneOf` 表达不同操作的独立参数合同: 1. 根据任务选择工具。 2. `moviepilot_api` 按 `properties.operation_id.const` 选择分支;其余三个入口按 `properties.action.const` 选择分支。 3. 根据该分支的 `required`、`properties`、`enum`、默认值和跨字段规则构造参数。分支中的 `$ref` 应在**整份 Schema** 的 `$defs` 中解析。 4. 只传该操作声明的字段;JSON 字符串、数字、布尔值、数组、对象和 `null` 必须保持正确类型。 5. 若已获得完整参数合同且目标明确,直接调用。无需为每次操作额外执行能力探测,也不要用缺参请求试探写操作。 外部 MCP 客户端不需要访问 MoviePilot 本地文件,也不会自动获得内置 Agent 的 Skill 内容。工具说明中提到的 Skill 可作为补充阅读;接入所需参数以当前部署返回的 Schema 为准。 ### 紧凑模式:为受 Schema 体积限制的客户端使用 在 MCP 请求头中增加: ```text X-MCP-Schema-Mode: compact ``` 该模式只改变工具发现时的 Schema 展示:超过客户端压缩阈值的大型 Schema 会去掉组合分支和定义表,保留顶层对象、必填字段及 operation/action 枚举。发生投影时会出现 `x-moviepilot-schema-projection` 标记。 **紧凑模式不包含每个操作的完整参数,也不承诺 Schema 一定小于客户端体积限制。** 服务端实际参数校验、身份与权限规则不变。调用前通过以下 HTTP 辅助端点取得完整 Schema,或复用已缓存且与当前部署一致的合同: ```bash curl -sS "$MP_MCP_URL/tools/moviepilot_api/schema" \ -H "X-API-KEY: $MP_API_KEY" ``` 这个辅助端点属于 REST,完整 Schema 位于响应的 **`data`**。如果客户端侧有 `jq`,可只保留所需分支及其依赖定义,减少交给模型的内容: ```bash curl -sS "$MP_MCP_URL/tools/moviepilot_api/schema" \ -H "X-API-KEY: $MP_API_KEY" \ | jq --arg op 'media.detail' '.data | .oneOf |= map(select(.properties.operation_id.const == $op))' ``` 上例假设 `MP_MCP_URL` 不包含查询参数;使用 URL 密钥认证时,需先构造完整路径,再附加 `?apikey=...`。不要向 `tools/call` 提交 Schema 或把 `$defs` 当成业务参数。 ## 4. 选择工具 | 任务 | 主要入口 | 选择依据 | | --- | --- | --- | | 媒体搜索、识别、订阅、站点资源搜索、下载、整理、历史、站点、插件、配置、调度、工作流 | `moviepilot_api` | MoviePilot 自身业务流程的首选入口 | | 下载器任务、文件选择、队列、标签、限速、Tracker、校验 | `downloader_operation` | 直接操作已配置下载器的原生能力 | | 媒体库浏览、播放会话、近期入库、扫描和元数据刷新 | `mediaserver_operation` | 直接操作已配置媒体服务器的原生能力 | | 数据库表、实时字段、SQL 统计与明确授权的数据修复 | `database_operation` | 业务 API 无法满足时使用;普通配置修改优先走业务 API | | 插件或其它运行时扩展能力 | `tools/list` 中对应工具 | 按返回的精确名称、说明和 Schema 调用 | 不要由此推断任意 REST 路由、第三方 API 或本地命令都能通过 MCP 调用。目录未提供的能力应明确说明不可用。`execute_command`、文件读写、`read_skill` 等内部工具不向 MCP 开放。 ### 4.1 `moviepilot_api`:产品业务操作 工具参数最多使用以下四个顶层字段,具体是否允许或必填由所选分支决定: | 字段 | 含义 | | --- | --- | | `operation_id` | 精确的白名单操作标识,例如 `media.search`、`subscription.list` | | `path_params` | 资源标识等路径参数 | | `query` | 筛选、分页等查询参数 | | `body` | 该操作声明的 JSON 请求体 | 以下示例均为 `tools/call` 的 **`params`**,发送时仍需外包 `jsonrpc`、`id`、`method`。示例标识必须替换为前序查询返回的真实标识。 搜索媒体: ```json { "name": "moviepilot_api", "arguments": { "operation_id": "media.search", "query": {"title": "盗梦空间", "media_source": ["tmdb"], "page": 1, "count": 5} } } ``` 按来源和媒体标识读取详情: ```json { "name": "moviepilot_api", "arguments": { "operation_id": "media.detail", "path_params": {"media_id": "27205"}, "query": {"media_source": "tmdb", "type_name": "电影"} } } ``` 构造业务调用时遵守以下规则: - 不传 `url`、`method`、请求头或认证凭据;固定 operation 由服务端映射到业务接口。 - 不将 `media_id` 等业务字段平铺到顶层,也不为无请求体的操作补 `body: {}`。 - `body` 使用原生 JSON 对象或数组;只有合同允许时才传 `null`。当前字符串请求体仅用于 `system.upgrade.dev` 的固定值 `"dev"`。 - 若模型把整个 `body` 编码成 JSON 字符串,各工具入口会在校验和发送前还原一层对象、数组或 `null`;嵌套字符串保持原值,非法请求体仍按 operation 合同拒绝。客户端应直接传原生 JSON 值。 - 媒体标识必须与返回它的 `media_source` 配对。不同操作的类型字段与枚举可能不同,不要把 `movie`、`电影` 或 `type`、`mtype`、`type_name` 相互套用。 - 下载、整理、订阅等操作优先复用前序查询返回的媒体上下文、种子上下文、存储项目和精确 ID,不根据标题猜测资源身份或文件路径。 业务操作的完整分类参考见 [MoviePilot API Skill](../skills/moviepilot-api/SKILL.md) 和其 [API 分类目录](../skills/moviepilot-api/api/)。运行时 Schema 才是目标实例的可调用范围。 ### 4.2 `downloader_operation` / `mediaserver_operation`:原生服务操作 下载器示例: ```json { "name": "downloader_operation", "arguments": { "client": "main-qb", "action": "tasks.list", "arguments": {"status": "downloading", "offset": 0, "limit": 20} } } ``` 媒体服务器示例: ```json { "name": "mediaserver_operation", "arguments": { "server": "living-room", "action": "activity.latest", "arguments": {"limit": 20} } } ``` 注意这里有两层 `arguments`:外层是 MCP 工具输入,内层是所选 `action` 的参数。 - `client` / `server` 是 MoviePilot 中已配置实例的精确名称,不是地址、类型或凭据。 - 已知实例名称时直接传入;未知时可省略。下载器使用默认或唯一启用实例,媒体服务器使用唯一启用实例。存在歧义时结果会列出有效名称,按用户目标选择后再调用。 - 若需要主动查看实例,使用 `action: "instances.list"`;查看一个动作的能力与参数时使用 `action: "capabilities.list"`,内层参数为 `{"action_name":"<精确动作>"}`。 - 动作是否可用取决于实际提供方。下载器任务 ID、媒体服务器项目 ID 与 MoviePilot 业务记录 ID 不可互换。 - 原生服务操作的结果只证明该服务的状态;例如下载器提交成功不等于 MoviePilot 已完成整理或入库。 完整动作参考:[下载器](../skills/downloader-operation/SKILL.md)、[媒体服务器](../skills/mediaserver-operation/SKILL.md)。 Emby、Jellyfin、Plex、Navidrome 的目录、条目、搜索、季集和活动查询支持内层可选 `query`,例如 `{"parent":"movies","limit":50,"query":{"fields":["identity","path","year"]}}`。 `fields` 省略或为 null 时保留原内容;空数组只取当前操作必需的信息。输入 schema 从 `MediaServerQueryOptions` 推导,内容组与服务端能力边界见 [媒体查询内容契约](media-server-query.md)。 调用方不能据此假定每种私有协议都支持完整的远端字段裁剪。 ### 4.3 `database_operation`:直接数据库访问 先发现真实表名和字段,再构造 SQL: ```json { "name": "database_operation", "arguments": {"action": "tables", "arguments": {}} } ``` | `action` | 内层 `arguments` | | --- | --- | | `tables` | 空对象,返回实际表清单 | | `schema` | `table_name`,必须使用表清单中的精确名称 | | `query` | `sql` 与 `file` 二选一;`limit` 默认 100,范围 1–200;默认只读,`write` 默认 `false`;`show_secrets` 默认 `false` | | `write` | `sql` 与 `file` 二选一;执行单条写入或结构变更语句,必须已有对应用户授权;引用 `systemconfig` 时同样需要 `show_secrets=true` | `file` 指 MoviePilot 运行环境中的 SQL 文件,不是客户端本地文件;外部接入通常直接传 `sql`。工具自行读取 MoviePilot 数据库配置,不接受任意连接串或数据库密码。 `systemconfig` 集中存放实例级 API Key、Cookie 与口令,语句只要引用该表(含引号标识符、模式限定名和 CTE 写法)就被拒绝,除非显式传 `show_secrets=true`。该门禁按表生效,因此计数或只取键名也需要开启;宿主会把这类读取判定为敏感读取并要求确认,回执中不保留结果内容。读取普通设置仍应使用 `config.system.*`。 SQLite 另在执行连接上校验真实表访问,单引号表名与视图转引也不能绕过;语句结束后撤销临时授权回调。 读取行时使用必要字段和有限窗口;写入前核对目标与条件,完成后只读核验。普通设置修改使用 `config.system.*`,避免绕过配置校验、事件通知和专用业务流程。更多约束见[数据库操作参考](../skills/database-operation/SKILL.md)。 ## 5. 组合业务调用 以下是调用顺序指引。具体参数仍按各 operation 的 Schema 构造,不将顺序中的操作全部默认执行。 | 用户目标 | 调用路径与完成依据 | | --- | --- | | 查找影片并查看详情 | `media.search` → 用户目标对应的精确媒体身份 → `media.detail` | | 检查库存后订阅 | 确认媒体身份 → `library.exists` / `subscription.find` → 按已授权意图调用 `subscription.add` → 查询订阅核验 | | 搜索并下载站点资源 | `search.torrents` 或 `search.title` → 从结果选择种子上下文 → `download.add` → 查询下载任务;下载和整理状态另行核验 | | 搜索已有订阅 | 读取订阅 → `subscription.search` → 按返回批次标识调用 `subscription.execution.get`;不要把触发搜索当成下载完成 | | 修改系统设置 | `config.system.list` 发现精确键 → `config.system.describe` 读取合同、当前值与 `revision` → `config.system.update` → 重新读取核验 | | 操作插件能力 | `plugin.installed` / `plugin.capabilities` 或当前工具目录 → 明确插件实例 → 对应业务 operation 或动态工具 → 查询结果 | 补充规则: - 设置更新优先回传 `expected_revision`。发生冲突时重新读取并比较,不直接覆盖;`generic_write_allowed=false` 时使用返回的专用操作。 - 插件存在分身时,显式选择目标实例或使用已经配置的默认目标。遇到实例歧义、停用目标或插件门禁拒绝时,按错误处理,不猜测另一个实例。 - 参数省略、空字符串与 `null` 的含义由具体操作决定。例如更新订阅时,省略字段通常保留原值,允许清空的字段可使用空值;不要把整份读取结果不加筛选地回写。 - `agent_task` 更新的 `enabled:null` 保留原启用状态,仅重命名暂停任务不会恢复调度。MCP 工具发现和 Google 模型发现会读取全部分页;重复游标或超过分页上限时报告失败。 - `agent_task` 创建时固定真实执行账号;渠道入口须唯一绑定启用账号。后台查询与管理员操作沿用同一账号权限,身份失效记录为失败。升级前的任务须从原会话 `update` 确认绑定或重新创建,详见[自主任务执行账号](agent-execution.md#自主任务执行账号)。 - 传统消息会话按渠道、配置接入源与原始用户 ID 隔离,停止、清除、状态和确认操作使用同一范围。WebAgent 后台完成追加本轮展示历史,前端完整快照仍按原合同替换。 - Agent 文件工具的禁止覆盖创建使用排他提交;同目标并发版本修改只允许仍匹配原 SHA 的请求提交。冲突后重新读取当前文件再编辑。`view_image` 使用只读权限,普通用户可读取允许范围内的公共技能图片。 - 安装市场技能保留用户明确选择的 GitHub 分支,该分支不可用时不改装默认分支。Agent 运行时人格文件变化使缓存失效;关闭 Agent 配置会通过能力 owner 停止服务。 - 订阅身份包含用户名、媒体类型、来源、媒体 ID、季号、剧集组和音乐实体类型。同编号的电影与电视剧可分别订阅;按来源定位时保留剧集组。同用户并发创建同一目标会复用真实订阅 ID。 - 更新订阅的作品、季号、剧集组或音乐实体后,旧目标的下载与洗版进度在同一事务清除,缺失集数按新总集数重新开始;显式清空剧集组也属于目标切换。目标身份已存在时取消该次修改,原有两条订阅及进度保持不变。 - Servarr 重复添加已监控的剧集返回真实既有订阅 ID。搜索任务取消后若执行者丢失,租约到期会结算取消并释放活动占位,再次搜索可重新入队。 - 工作流恢复会重试失败节点及因失败跳过的节点,成功节点继续复用结果;文本 `content` 会随其他上下文完整恢复。编辑暂停工作流不会重新注册定时器,Cron 使用宿主调度时区;并行分支保存去重缓存时合并各自记录。 - 自定义过滤规则的新增、修改或删除遇到并发快照冲突时返回业务失败。先重新读取规则集合,再重新确认修改,不用旧集合覆盖已成功提交的配置。 - `search.torrents` 可能执行多轮站点搜索,工具等待上限为 300 秒;客户端应为此设置足够的超时。分页只限制返回结果,不会缩短搜索过程。 ## 6. 处理结果、分页与失败恢复 ### 分层判断成功 客户端依次检查三层结果: 1. **HTTP 与 JSON-RPC:**检查状态码及顶层 `error`。认证或协议失败时不进入业务结果解析;通知成功的 `204` 没有正文。 2. **MCP 工具:**检查 `result.isError`,读取 `result.content` 中的文本块。当前工具返回文本,不能假定存在 `structuredContent`。 3. **业务与执行状态:**文本为 JSON 时检查 `success: false`、`error`、`execution_outcome` 及具体业务状态。即使 `isError` 缺失,文本内仍可能是业务失败。 `execution_outcome: "pending"` 表示已提交但尚未确认最终完成;`"unknown"` 表示效果未知,先用只读操作核验实际状态。没有该字段时,依据具体业务响应判断,不能自动推断异步任务完成。 `media.scrape` 遇到不支持的媒体文件或 NFO 保存失败时返回 `success: false` 及原因,目录中的媒体保存失败也会向上传递。不要将这种响应报告为刮削完成。 ### 分页与截断 - 按选定操作的合同使用分页字段。例如 `subscription.list` 使用 `page` / `count`,`subscription.execution.list` 使用 `limit`,原生服务列表常用 `offset` / `limit`。 - 支持兼容分页的列表,两项均省略时仍可能返回全量;显式传 `page` 或 `count` 才开启分页,缺失值分别按 1 和 50 处理,`count` 最大 200。 - `moviepilot_api` 的 `collection` 是附加元数据,不替换 `data`。`result_count` 是本次返回数量,`total_count` 仅在服务端知道精确总数时出现;缺失不等于零。 - 只求总数时,对提供精确总数的操作请求最小窗口,例如 `page=1,count=1`,读取 `collection.total_count`。不要将当前页长度当成总数。 - 工具结果可能被截断。看到 `tool_result_truncated: true` 时,`content_preview` 只是预览;按回执提示缩小查询、分页,或在提供结果引用与读取工具时继续读取。不要因预览不完整而重新执行写操作。 ### 常见失败 | 信号 | 处理方式 | | --- | --- | | HTTP `401` | 检查地址对应实例、`API_TOKEN`、请求头是否被代理保留,以及超级管理员账号是否有效 | | HTTP `503` | 认证等运行时服务可能尚未初始化;先检查实例就绪状态 | | JSON-RPC `-32700` | 请求正文不是有效 JSON | | JSON-RPC `-32600` | 请求不是 JSON-RPC 2.0 对象,或错误地给 initialized 通知添加了 `id` | | JSON-RPC `-32601` | 方法未实现;检查是否误用旧 SSE、resources 或 prompts 接口 | | JSON-RPC `-32602` | 请求参数不符合协议入口要求,按错误详情修正 | | JSON-RPC `-32603` | 服务端内部错误,保留请求 ID 与脱敏错误信息排查 | | 业务 `invalid_input` | 按返回的 `input_contract` 一次修正缺失、位置、类型或未声明字段;不要添加猜测参数 | | 工具或 operation 不存在 | 重新读取工具目录和完整 Schema,选择当前部署提供的能力 | | 权限拒绝或配置修订冲突 | 核对身份、授权范围或当前修订,不能通过 SQL 或另一个接口绕过 | | 超时、连接中断、`pending` / `unknown` | 对有副作用的操作先查实际状态,避免重复下载、订阅、删除或提交任务 | 网关参数校验失败通常携带 `input_contract`;更外层的解析失败或其它工具不保证提供该字段,必要时回到完整 Schema。不要把全部失败归结为可直接重试的网络问题。 ## 7. HTTP 辅助端点 不使用 MCP SDK 的程序也可以通过这些端点发现和调用同一套工具。所有端点使用相同 API Key 认证,响应遵循普通 REST 包装 **`{"success": ..., "message": ..., "data": ...}`**,与 `/mcp` 的 JSON-RPC 包装不同。 | 方法与路径 | 请求或返回内容 | | --- | --- | | `GET /api/v1/mcp/tools` | `data` 为工具数组,包含完整 `inputSchema`;可选 `page` / `count` | | `GET /api/v1/mcp/tools/{tool_name}` | `data` 为单个工具的名称、说明与完整 `inputSchema` | | `GET /api/v1/mcp/tools/{tool_name}/schema` | `data` 为完整输入 Schema,即使 MCP 使用 compact 也不受影响 | | `POST /api/v1/mcp/tools/call` | 请求字段为 `tool_name`、`arguments`;成功分发时 `data.result` 为工具结果文本 | REST 调用示例: ```json { "tool_name": "moviepilot_api", "arguments": {"operation_id": "subscription.list", "query": {"page": 1, "count": 20}} } ``` 注意 REST 使用 `tool_name`,MCP `tools/call.params` 使用 `name`。REST 外层 `success: true` 同样不代表内层业务成功,仍需解析 `data.result`。 ## 8. 参考与维护 - [业务 API 操作参考](../skills/moviepilot-api/SKILL.md):按领域查阅业务操作与约束。 - [下载器操作参考](../skills/downloader-operation/SKILL.md)、[媒体服务器操作参考](../skills/mediaserver-operation/SKILL.md)、[数据库操作参考](../skills/database-operation/SKILL.md):原生服务与 SQL 操作合同。 本文维护连接方式、发现流程、调用结构和结果处理。字段清单、操作全集、提供方差异由运行时 Schema 及对应参考维护;新增业务能力时不应继续向本文追加 REST 路由表、前端展示契约或版本变更记录。