EmDash 在 /_emdash/api/mcp 提供内置 模型上下文协议(MCP)服务器,将内容管理操作作为工具暴露给 AI 助手。
本文说明协议层面内容:身份验证、传输、工具规范、OAuth 发现与错误处理。
身份验证
MCP 服务器支持三种身份验证方式:
| 方式 | 工作原理 |
|---|---|
| OAuth 2.1 授权码 + PKCE | MCP 客户端的标准流程。用户在浏览器中批准权限范围(scope)。 |
| 个人访问令牌(PAT) | 在管理后台创建的长期有效 ec_pat_* 令牌。 |
| 设备码流程 | 类似 CLI 的流程,在浏览器中批准验证码。由 emdash login 使用。 |
管理后台产生的会话 Cookie 也可用,但对外部 MCP 客户端不实用。
权限范围(Scopes)
令牌通过 scope 限制客户端可执行的操作。scope 在 OAuth 授权时申请,并在每次工具调用时强制校验。
| Scope | 授予的权限 |
|---|---|
content:read | 列出、获取、比较与搜索内容。列出分类法、分类项与菜单。 |
content:write | 创建、更新、删除、发布、撤下发布、定时发布、取消定时、复制与还原内容。为兼容在拆分出下列 scope 之前签发的令牌,隐式包含 taxonomies:manage 与 menus:manage。 |
media:read | 列出并获取媒体项。 |
media:write | 登记(创建)、更新与删除媒体元数据。 |
schema:read | 列出集合并获取集合结构(schema)。 |
schema:write | 创建与删除集合及字段。 |
taxonomies:manage | 创建、更新与删除分类项。 |
menus:manage | 创建、更新与删除导航菜单及其条目。 |
settings:read | 读取站点级设置。 |
settings:manage | 更新站点级设置。 |
admin | 对所有操作的完全访问。 |
admin scope 可访问一切。基于会话的身份验证(无令牌)也会根据用户角色拥有与其角色相符的全部权限。
content:write 隐式包含 taxonomies:manage 与 menus:manage,以便在拆分这些 scope 之前签发的个人访问令牌无需重新签发仍可工作。新签发的令牌应申请更细粒度的 scope。
角色要求
除 scope 外,部分工具还要求最低 RBAC 角色。两者须同时满足——即便令牌 scope 正确,若调用用户角色不足仍会失败。
| 操作 | 最低角色 |
|---|---|
| 内容读取 | 已发布条目:订阅者(10);草稿、定时、回收站与修订:贡献者(20) |
| 内容创建 / 编辑自己的 / 删除自己的 | 作者(30) |
| 内容发布 | 自己的条目:作者(30);操作他人条目:编辑(40) |
| 结构读取 | 编辑(40) |
| 结构写入 | 管理员(50) |
| 分类管理 | 编辑(40) |
| 菜单管理 | 编辑(40) |
| 设置读取 | 编辑(40) |
| 设置管理 | 管理员(50) |
媒体上传(media_create) | 作者(30) |
角色定义见 身份验证指南。
传输
服务器在 无状态模式 下使用可流式 HTTP(Streamable HTTP)传输。每个请求相互独立——无会话、无长连接。
POST /_emdash/api/mcp— 发送 JSON-RPC 工具调用GET /_emdash/api/mcp— 返回 405(无状态模式下无 SSE)DELETE /_emdash/api/mcp— 返回 405(无可关闭的会话)
响应遵循 JSON-RPC 2.0。错误使用标准 JSON-RPC 错误码;scope 与权限失败时另有 MCP 专用错误码。
工具
服务器在八个领域中暴露 43 个工具:内容、结构(schema)、媒体、搜索、分类法、菜单、修订与设置。成功时每个工具以 JSON 文本内容返回结果;失败时返回错误信息并带 isError: true。
内容工具
content_list
在集合中列出内容项,支持可选筛选与分页。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug(如 posts、pages) |
status | string | 否 | 筛选:draft、published 或 scheduled |
limit | integer | 否 | 最大返回条数(1–100,默认 50) |
cursor | string | 否 | 上一页响应中的分页游标 |
orderBy | string | 否 | 排序字段(如 created_at、updated_at) |
order | string | 否 | 排序方向:asc 或 desc(默认 desc) |
locale | string | 否 | 按语言区域筛选(如 en、fr)。仅在启用 i18n 时有效。 |
Scope: content:read | 只读: 是
content_get
按 ID 或 slug 获取单条内容。返回全部字段值、元数据,以及用于乐观并发控制的 _rev 令牌。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID(ULID)或 slug |
locale | string | 否 | 通过 slug 查找时的语言区域。ID 全局唯一。 |
Scope: content:read | 只读: 是
content_create
新建内容项。data 对象中的字段值须符合集合 schema——可用 schema_get_collection 查看可用字段。默认以 draft 创建。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
data | object | 是 | 字段键值对 |
slug | string | 否 | URL slug(省略时通常由标题生成) |
status | string | 否 | 初始状态:draft 或 published(默认 draft) |
locale | string | 否 | 该内容的语言区域(默认站点默认语言) |
translationOf | string | 否 | 若本条为翻译,所指向的源条目 ID |
Scope: content:write
content_update
更新已有内容。只传需要修改的字段——未出现的字段保持不变。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID 或 slug |
data | object | 否 | 要更新的字段值 |
slug | string | 否 | 新的 URL slug |
status | string | 否 | 新状态:draft 或 published |
_rev | string | 否 | 来自 content_get 的修订令牌,用于冲突检测 |
Scope: content:write
content_delete
软删除:将内容移入回收站。可用 content_restore 恢复,或用 content_permanent_delete 永久删除。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID 或 slug |
Scope: content:write | 破坏性: 是
content_restore
从回收站恢复已软删除的内容项。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID 或 slug |
Scope: content:write
content_permanent_delete
永久且不可逆地删除已在回收站中的内容项。条目须先位于回收站。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID 或 slug |
Scope: content:write | 破坏性: 是
content_publish
发布内容,使其在站点上线。会基于当前草稿创建已发布修订。后续编辑产生新草稿,在再次发布前不会影响线上版本。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID 或 slug |
Scope: content:write
content_unpublish
将已发布条目改回草稿,站点前台不再展示,但内容仍保留。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID 或 slug |
Scope: content:write
content_schedule
为内容项安排未来发布时间,到点自动发布。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID 或 slug |
scheduledAt | string | 是 | ISO 8601 日期时间(如 2026-06-01T09:00:00Z) |
Scope: content:write
content_unschedule
取消已安排的定时发布。条目保持当前状态;仅清除 scheduledAt 时间戳。幂等:对未安排发布的条目调用无效果。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID 或 slug |
Scope: content:write
content_compare
比较内容项的已发布(线上)版本与当前草稿。返回两个版本及是否有差异的标记。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID 或 slug |
Scope: content:read | 只读: 是
content_discard_draft
丢弃当前草稿,回退到最后一次已发布版本。仅适用于至少发布过一次的条目。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID 或 slug |
Scope: content:write | 破坏性: 是
content_list_trashed
列出集合回收站中的软删除内容。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
limit | integer | 否 | 最大条数(1–100,默认 50) |
cursor | string | 否 | 分页游标 |
Scope: content:read | 只读: 是
content_duplicate
复制已有内容项。副本以草稿创建,标题附加「(Copy)」,slug 自动生成。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 要复制的内容项 ID 或 slug |
Scope: content:write
content_translations
获取某内容项在各语言区域下的变体。返回翻译分组及每个语言版本的摘要。仅在启用 i18n 时有效。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID 或 slug |
Scope: content:read | 只读: 是
结构(Schema)工具
schema_list_collections
列出 CMS 中定义的全部内容集合。返回 slug、标签、支持的能力与时间戳。
无参数。
Scope: schema:read | 最低角色: 编辑 | 只读: 是
schema_get_collection
获取集合详情,含全部字段定义。字段描述数据模型:名称、类型、约束与校验规则。用于理解 content_create 与 content_update 的预期。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
slug | string | 是 | 集合 slug(如 posts) |
Scope: schema:read | 最低角色: 编辑 | 只读: 是
schema_create_collection
新建内容集合。会创建数据库表与结构定义。slug 须为小写字母数字加下划线,且以字母开头。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
slug | string | 是 | 唯一标识(/^[a-z][a-z0-9_]*$/) |
label | string | 是 | 显示名称(复数,如 “Blog Posts”) |
labelSingular | string | 否 | 单数显示名称 |
description | string | 否 | 集合说明 |
icon | string | 否 | 管理后台使用的图标名 |
supports | string[] | 否 | 能力:drafts、revisions、preview、scheduling、search(默认:['drafts', 'revisions']) |
Scope: schema:write | 最低角色: 管理员
schema_delete_collection
删除集合及其数据库表。不可逆,会删除集合内全部内容。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
slug | string | 是 | 要删除的集合 slug |
force | boolean | 否 | 若集合仍有内容是否仍强制删除 |
Scope: schema:write | 最低角色: 管理员 | 破坏性: 是
schema_create_field
向集合结构添加新字段,即在表中新增列。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
slug | string | 是 | 字段标识(/^[a-z][a-z0-9_]*$/) |
label | string | 是 | 显示名称 |
type | string | 是 | 数据类型(见下) |
required | boolean | 否 | 是否必填 |
unique | boolean | 否 | 是否要求值唯一 |
defaultValue | any | 否 | 新条目的默认值 |
validation | object | 否 | 约束:min、max、minLength、maxLength、pattern、options |
options | object | 否 | 控件配置:collection(引用)、rows(多行文本) |
searchable | boolean | 否 | 是否纳入全文检索索引 |
translatable | boolean | 否 | 是否可翻译(默认 true) |
字段类型:string、text、number、integer、boolean、datetime、select、multiSelect、portableText、image、file、reference、json、slug。
select 与 multiSelect 的允许取值放在 validation.options 中。
Scope: schema:write | 最低角色: 管理员
schema_delete_field
从集合移除字段。会删除列及该列全部数据。不可逆。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
fieldSlug | string | 是 | 要移除的字段 slug |
Scope: schema:write | 最低角色: 管理员 | 破坏性: 是
媒体工具
media_list
列出已上传的媒体文件,支持按 MIME 类型前缀筛选与分页。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
mimeType | string | 否 | MIME 类型前缀(如 image/、application/pdf) |
limit | integer | 否 | 最大条数(1–100,默认 50) |
cursor | string | 否 | 分页游标 |
Scope: media:read | 只读: 是
media_create
登记已上传到存储的媒体文件。调用方须负责将文件放到 storageKey(通常使用管理后台或独立 API 返回的签名上传 URL)。该工具持久化元数据,使文件可通过 media_list / media_get 发现并被内容引用。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
filename | string | 是 | 原始文件名(如 logo.png) |
mimeType | string | 是 | MIME 类型(如 image/png) |
storageKey | string | 是 | 文件在存储中的路径/键 |
size | integer | 否 | 文件大小(字节) |
width | integer | 否 | 图片宽度(像素) |
height | integer | 否 | 图片高度(像素) |
contentHash | string | 否 | 内容哈希(去重用) |
blurhash | string | 否 | 图片占位用 Blurhash |
dominantColor | string | 否 | 主色十六进制色值 |
Scope: media:write | 最低角色: 作者
media_get
按 ID 获取单个媒体文件的详情。返回文件名、MIME 类型、尺寸、替代文本、URL 等元数据。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | 媒体项 ID |
Scope: media:read | 只读: 是
media_update
更新已上传媒体文件的元数据。文件二进制不可更换。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | 媒体项 ID |
alt | string | 否 | 无障碍替代文本 |
caption | string | 否 | 说明文字 |
width | integer | 否 | 图片宽度(像素) |
height | integer | 否 | 图片高度(像素) |
Scope: media:write
media_delete
永久删除媒体文件:删除数据库记录与存储中的文件。引用该媒体的内容将出现坏链。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | 媒体项 ID |
Scope: media:write | 破坏性: 是
搜索工具
search
跨内容集合全文搜索。集合的 supports 须包含 search,且字段须标记为 searchable。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
query | string | 是 | 搜索关键词 |
collections | string[] | 否 | 仅搜索指定集合 slug |
locale | string | 否 | 按语言区域筛选结果 |
limit | integer | 否 | 最大结果数(1–50,默认 20) |
Scope: content:read | 只读: 是
分类法工具
taxonomy_list
列出全部分类法定义(如分类、标签)。返回名称、标签、是否层级式及关联集合。
无参数。
Scope: content:read | 只读: 是
taxonomy_list_terms
分页列出某分类法下的词条。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
taxonomy | string | 是 | 分类法名称(如 categories、tags) |
limit | integer | 否 | 最大条数(1–100,默认 50) |
cursor | string | 否 | 分页游标 |
Scope: content:read | 只读: 是
taxonomy_create_term
在分类法下新建词条。层级式分类法可指定 parentId 创建子项。父级祖先链深度不得超过 100 层。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
taxonomy | string | 是 | 分类法名称 |
slug | string | 是 | URL 安全标识 |
label | string | 是 | 显示名称 |
parentId | string | 否 | 父词条 ID(层级式分类法) |
description | string | 否 | 词条说明 |
Scope: taxonomies:manage | 最低角色: 编辑
taxonomy_update_term
更新分类法中已有词条。可省略字段以保持原值。重命名 slug 不得与同分类法下其他词条冲突。将 parentId 设为 null 可脱离父级。新父级须存在、同属该分类法,且不得产生环。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
taxonomy | string | 是 | 分类法名称 |
termSlug | string | 是 | 待更新词条的当前 slug |
slug | string | 否 | 新 slug(在分类法内唯一) |
label | string | 否 | 新显示名称 |
parentId | string | null | 否 | 新父词条 ID;null 表示脱离父级 |
description | string | 否 | 新说明 |
Scope: taxonomies:manage | 最低角色: 编辑
taxonomy_delete_term
从分类法永久删除词条。曾打上该标签的内容将失去关联。若词条仍有子项则不可删——须先删除子项。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
taxonomy | string | 是 | 分类法名称 |
termSlug | string | 是 | 要删除词条的 slug |
Scope: taxonomies:manage | 最低角色: 编辑 | 破坏性: 是
菜单工具
menu_list
列出全部导航菜单。返回名称、标签与时间戳。
无参数。
Scope: content:read | 只读: 是
menu_get
按名称获取菜单及其有序条目。条目含标签、URL、类型及可选父级以支持嵌套。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 是 | 菜单名称(如 main、footer) |
Scope: content:read | 只读: 是
menu_create
新建导航菜单。name 为站点模板使用的稳定标识;label 为管理后台展示名。条目稍后通过 menu_set_items 添加。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 是 | 稳定标识(/^[a-z][a-z0-9_]*$/) |
label | string | 是 | 管理后台显示名称 |
Scope: menus:manage | 最低角色: 编辑
menu_update
更新菜单的标签。稳定标识 name 不可改。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 是 | 要更新的菜单名称 |
label | string | 是 | 新显示标签 |
Scope: menus:manage | 最低角色: 编辑
menu_delete
删除菜单及其全部条目。不可恢复。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 是 | 要删除的菜单名称 |
Scope: menus:manage | 最低角色: 编辑 | 破坏性: 是
menu_set_items
单次调用替换菜单的全部条目。原子操作:先删旧条目,再按给定顺序插入新列表。比逐条增删更清晰,顺序与父子关系明确。
条目位置由数组下标决定。嵌套通过 parentIndex 表达——parentIndex: 0 表示嵌套在索引 0 的条目下。父项须出现在列表更前位置。无 parentIndex 的为顶级项。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 是 | 要更新的菜单名称 |
items | MenuItem[] | 是 | 有序菜单条目列表(见下) |
每个 MenuItem 字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
label | string | 是 | 显示文本 |
type | string | 是 | custom、page、post、taxonomy、collection 之一 |
customUrl | string | 否 | type: "custom" 时的 URL(其他类型忽略) |
referenceCollection | string | 否 | 内容引用目标集合 slug |
referenceId | string | 否 | 引用目标内容/词条 ID |
titleAttr | string | 否 | HTML title 属性 |
target | string | 否 | HTML target(如 _blank) |
cssClasses | string | 否 | 空格分隔的 CSS 类名 |
parentIndex | integer | 否 | 父条目在数组中的索引;顶级项省略 |
Scope: menus:manage | 最低角色: 编辑
修订工具
revision_list
列出内容项的修订历史,新的在前。集合须启用 revisions。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | string | 是 | 集合 slug |
id | string | 是 | 内容项 ID 或 slug |
limit | integer | 否 | 最大修订条数(1–50,默认 20) |
Scope: content:read | 只读: 是
revision_restore
将内容项恢复到某次修订:用指定修订的数据替换当前草稿。不会自动发布——需要时请再调用 content_publish。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
revisionId | string | 是 | 要恢复的修订 ID |
Scope: content:write
设置工具
站点级设置——标题、标语、Logo、网站图标、规范 URL、默认分页大小、日期与时间格式、社交账号及 SEO 默认值等。
settings_get
获取全部站点级设置。媒体引用(logo、favicon、seo.defaultOgImage)在返回中含解析后的 URL 及底层 mediaId。未设置的值不出现在响应中。
无参数。
Scope: settings:read | 最低角色: 编辑 | 只读: 是
settings_update
更新一项或多项站点级设置。部分更新:仅修改提供的字段;未提供的保持不变。返回更新后的完整设置对象。
设置媒体引用(logo、favicon、seo.defaultOgImage)时传入带 mediaId(及可选 alt)的对象。媒体项须已存在——请先 media_create。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
title | string | 否 | 站点标题 |
tagline | string | 否 | 标题旁简短描述 |
logo | MediaRef | 否 | Logo 媒体引用({ mediaId, alt? }) |
favicon | MediaRef | 否 | 网站图标媒体引用 |
url | string | 否 | 站点规范 URL(http 或 https)。空字符串表示清空。 |
postsPerPage | integer | 否 | 内容列表默认每页条数(1–100) |
dateFormat | string | 否 | 日期格式占位串 |
timezone | string | 否 | IANA 时区标识 |
social | object | 否 | 社交账号——twitter、github、facebook、instagram、linkedin、youtube |
seo | object | 否 | SEO 默认值(见下) |
seo 对象字段:
| 字段 | 类型 | 说明 |
|---|---|---|
titleSeparator | string | 页面标题与站点标题之间的分隔符(如 " | " 表示竖线) |
defaultOgImage | MediaRef | 内容无图时的默认 Open Graph 图片 |
robotsTxt | string | 自定义 robots.txt 正文。省略则使用 EmDash 默认。 |
googleVerification | string | Google Search Console 验证令牌 |
bingVerification | string | Bing 网站管理员工具验证令牌 |
Scope: settings:manage | 最低角色: 管理员
OAuth 发现
支持 OAuth 2.1 的 MCP 客户端可自动发现如何认证。服务器发布两份元数据:
受保护资源元数据
GET /.well-known/oauth-protected-resource
{
"resource": "https://example.com/_emdash/api/mcp",
"authorization_servers": ["https://example.com/_emdash"],
"scopes_supported": [
"content:read", "content:write",
"media:read", "media:write",
"schema:read", "schema:write",
"taxonomies:manage", "menus:manage",
"settings:read", "settings:manage",
"admin"
],
"bearer_methods_supported": ["header"]
}
授权服务器元数据
GET /.well-known/oauth-authorization-server/_emdash
{
"issuer": "https://example.com/_emdash",
"authorization_endpoint": "https://example.com/_emdash/oauth/authorize",
"token_endpoint": "https://example.com/_emdash/api/oauth/token",
"scopes_supported": ["content:read", "content:write", "..."],
"response_types_supported": ["code"],
"grant_types_supported": [
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:device_code"
],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none"],
"device_authorization_endpoint": "https://example.com/_emdash/api/oauth/device/code"
}
当未经认证的请求命中 MCP 端点时,服务器返回:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource"
由此触发标准 MCP 客户端发现流程。
错误处理
工具错误以文本内容返回,并带 isError: true:
{
"content": [{ "type": "text", "text": "Collection 'nonexistent' not found" }],
"isError": true
}
Scope 与权限错误以 MCP 协议错误形式抛出:
{
"jsonrpc": "2.0",
"error": {
"code": -32600,
"message": "Insufficient scope: requires content:write"
},
"id": 1
}
传输层错误(服务器配置错误、未处理异常等)返回 JSON-RPC 错误码 -32603(内部错误),且不暴露实现细节。