Files
MoviePilot/docs/mcp-api.md

23 KiB
Raw Blame History

MoviePilot MCP 接入说明

本文面向通过 MCP 调用 MoviePilot 的外部 Agent 和工具开发者。接入后,客户端通过工具目录发现能力,按 JSON Schema 构造参数,并根据业务结果判断操作是否完成。

**最短调用路径:**配置连接与密钥 → initialize → notifications/initialized → tools/list → tools/call。已有 MCP 客户端通常会自动完成初始化和工具发现,只需配置下表中的连接信息。

订阅下载的提交来源

新产生的订阅下载在订阅活动和日志中区分“首次订阅”“洗版升级”或“全集洗版”。 下载通知的标题和正文按通知模板渲染,不额外拼接来源标签。 该标记来自提交前订阅的已下载事实与最终选集,不改变过滤、优先级、选集和下载策略。 同一整包包含已下载集与缺集时计作一次洗版任务,分别保留 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 继续保留原入口信息。 下载处理上下文在持久事件恢复后保留该来源,已冻结的通知保持模板渲染结果。

升级前历史没有来源标记,界面显示“未记录”,不会按当前订阅设置追溯推断。 普通订阅的首次提交与洗版订阅补缺在活动中都标记为首次订阅。

1. 连接与认证

配置项 值与含义
服务地址 https://<MoviePilot 后端地址>/api/v1/mcp;使用实际可达的后端地址,反向代理有前缀时需一并保留
传输方式 Streamable HTTP,向同一个地址 POST JSON-RPC 2.0 消息
认证请求头 X-API-KEY: <API_TOKEN>,值为该 MoviePilot 实例配置的 API_TOKEN
请求类型 Content-Type: application/json
接受类型 客户端可发送 Accept: application/json, text/event-stream;当前服务端以 JSON 返回调用结果
Schema 模式 默认 full;客户端无法处理大型 Schema 时,增加 X-MCP-Schema-Mode: compact,详见参数合同

客户端必须能够配置远程 HTTP MCP URL 和自定义请求头。不同客户端的配置文件格式不同,将上述值填入其对应配置项即可。这里的服务地址是 HTTP 入口,不是本地进程启动命令。

认证补充:

  • MCP 使用固定 API 密钥,不使用网页登录的 Bearer JWT,也不提供 OAuth 登录流程。
  • 无法设置请求头的客户端可使用 .../api/v1/mcp?apikey=<API_TOKEN>。优先使用请求头,避免密钥进入 URL 日志与历史记录。
  • 每个请求都需要认证,包括初始化、发现工具和终止会话。密钥校验还要求实例中存在有效的超级管理员账号。
  • 这是管理员级集成入口,可以访问站点、配置、数据库及有副作用的操作。只向可信客户端提供密钥,并使用 HTTPS 或受信网络。
  • 写操作的用户授权由调用方负责取得;不要假设 MCP 会弹出 MoviePilot 内置 Agent 的确认界面。隐藏工具也不构成权限隔离。

下文的 HTTP 示例使用两个占位环境变量,由接入方在自己的环境中设置。不要把真实密钥写进文档、模型提示词或代码仓库。

MP_MCP_URL='https://moviepilot.example.com/api/v1/mcp'
MP_API_KEY='<该实例的 API_TOKEN>'

2. 完成一次最小调用

2.1 初始化

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:

{
  "jsonrpc": "2.0",
  "method": "notifications/initialized",
  "params": {}
}

使用与初始化相同的地址和认证头 POST。当前实现返回 HTTP 204,没有 JSON 正文。

2.2 发现工具

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 调用一个只读工具

下面查询第一页订阅,不创建或修改订阅:

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 中的内容为示意业务结果:

{
  "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 判断业务成功。完整处理规则见结果与恢复。

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 请求头中增加:

X-MCP-Schema-Mode: compact

该模式只改变工具发现时的 Schema 展示:超过客户端压缩阈值的大型 Schema 会去掉组合分支和定义表,保留顶层对象、必填字段及 operation/action 枚举。发生投影时会出现 x-moviepilot-schema-projection 标记。

紧凑模式不包含每个操作的完整参数,也不承诺 Schema 一定小于客户端体积限制。 服务端实际参数校验、身份与权限规则不变。调用前通过以下 HTTP 辅助端点取得完整 Schema,或复用已缓存且与当前部署一致的合同:

curl -sS "$MP_MCP_URL/tools/moviepilot_api/schema" \
  -H "X-API-KEY: $MP_API_KEY"

这个辅助端点属于 REST,完整 Schema 位于响应的 data。如果客户端侧有 jq,可只保留所需分支及其依赖定义,减少交给模型的内容:

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。示例标识必须替换为前序查询返回的真实标识。

搜索媒体:

{
  "name": "moviepilot_api",
  "arguments": {
    "operation_id": "media.search",
    "query": {"title": "盗梦空间", "media_source": ["tmdb"], "page": 1, "count": 5}
  }
}

按来源和媒体标识读取详情:

{
  "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"。
  • 媒体标识必须与返回它的 media_source 配对。不同操作的类型字段与枚举可能不同,不要把 movie、电影 或 type、mtype、type_name 相互套用。
  • 下载、整理、订阅等操作优先复用前序查询返回的媒体上下文、种子上下文、存储项目和精确 ID,不根据标题猜测资源身份或文件路径。

业务操作的完整分类参考见 MoviePilot API Skill 和其 API 分类目录。运行时 Schema 才是目标实例的可调用范围。

4.2 downloader_operation / mediaserver_operation:原生服务操作

下载器示例:

{
  "name": "downloader_operation",
  "arguments": {
    "client": "main-qb",
    "action": "tasks.list",
    "arguments": {"status": "downloading", "offset": 0, "limit": 20}
  }
}

媒体服务器示例:

{
  "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 已完成整理或入库。

完整动作参考:下载器、媒体服务器。

Emby、Jellyfin、Plex、Navidrome 的目录、条目、搜索、季集和活动查询支持内层可选 query,例如 {"parent":"movies","limit":50,"query":{"fields":["identity","path","year"]}}。 fields 省略或为 null 时保留原内容;空数组只取当前操作必需的信息。输入 schema 从 MediaServerQueryOptions 推导,内容组与服务端能力边界见 媒体查询内容契约。 调用方不能据此假定每种私有协议都支持完整的远端字段裁剪。

4.3 database_operation:直接数据库访问

先发现真实表名和字段,再构造 SQL:

{
  "name": "database_operation",
  "arguments": {"action": "tables", "arguments": {}}
}
action 内层 arguments
tables 空对象,返回实际表清单
schema table_name,必须使用表清单中的精确名称
query sql 与 file 二选一;limit 默认 100,范围 1–200;默认只读,write 默认 false
write sql 与 file 二选一;执行单条写入或结构变更语句,必须已有对应用户授权

file 指 MoviePilot 运行环境中的 SQL 文件,不是客户端本地文件;外部接入通常直接传 sql。工具自行读取 MoviePilot 数据库配置,不接受任意连接串或数据库密码。

读取行时使用必要字段和有限窗口;写入前核对目标与条件,完成后只读核验。普通设置修改使用 config.system.*,避免绕过配置校验、事件通知和专用业务流程。更多约束见数据库操作参考。

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 的含义由具体操作决定。例如更新订阅时,省略字段通常保留原值,允许清空的字段可使用空值;不要把整份读取结果不加筛选地回写。
  • 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" 表示效果未知,先用只读操作核验实际状态。没有该字段时,依据具体业务响应判断,不能自动推断异步任务完成。

分页与截断

  • 按选定操作的合同使用分页字段。例如 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 调用示例:

{
  "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. 参考与维护

本文维护连接方式、发现流程、调用结构和结果处理。字段清单、操作全集、提供方差异由运行时 Schema 及对应参考维护;新增业务能力时不应继续向本文追加 REST 路由表、前端展示契约或版本变更记录。