MCP Server
以 Model Context Protocol 暴露给 AI 客户端的只读检索接口,可选开放运维工具组
MCP Server 把站点已发布的文章以 Model Context Protocol 暴露给 AI 客户端(如 Claude、Cursor),让模型在不抓取网页的前提下检索站内公开内容,并可在配置令牌后开放线上运维工具。本节介绍端点位置、暴露的工具、运维工具组、配置开关与限速策略。
启用与关闭
MCP 端点的开关在 site.config.ts 的 features.mcp(默认 true)。改动该字段后必须重新 bun run build,开关在构建期烘焙到运行时配置;关闭后端点保持注册但一律返回 404,与高德地图服务端代理的关闭模式一致。
端点位置
POST /mcp 是唯一的 MCP 入口,采用 JSON-RPC 协议且不建立 session(stateless),每次请求都会新建一个 MCP Server 实例并就地销毁。端点位于根级 URL,不走 /api/* 前缀,因此不受 Referer 校验和后台访问日志约束。AI 客户端无浏览器 Referer,但端点本身已自带限速、只读白名单与字段筛选,公开检索无须额外门禁;唯一的写操作清缓存另受 MCP_OPS_TOKEN 令牌门禁,见 运维工具组。
非 POST 方法会得到 405 与 Allow: POST 响应头,请求体超过 1 MB 会直接被 SDK 拒收。
暴露的工具
默认暴露十九个只读检索工具,全部带 readOnlyHint: true,避免 prompt injection 把 AI 引导到删文章、改评论;配置 MCP_OPS_TOKEN 后还会追加五个运维工具,见下节。按 AI Agent 浏览站点的完整路径分组:
| 分组 | 工具 | 入参 | 用途 |
|---|---|---|---|
| 检索 | search_content | q: string、limit?: 1~20、offset?: ≥0 | 按关键词搜索已发布文章,返回标题、摘要、链接、发布时间与正文命中摘录,不含全文 |
| 阅读 | get_content | cid / slug 二选一、type?: post/page、raw?、offset?、maxChars? | 取一篇文章或独立页面的正文,Markdown 源码转纯文本后输出,长文用 offset 续读 |
| 阅读 | list_content_images | cid: number、limit?: 1~100 | 列出文章配图(封面 + 图片附件)的 URL 与尺寸,不含视频附件 |
| 浏览 | list_recent_contents | limit?: 1~50 | 列出最近发布的文章标题、链接、发布时间 |
| 浏览 | get_popular_contents | sort?: likes/comments、limit?: 1~20 | 热门文章,按点赞数或评论数倒序 |
| 浏览 | list_archives | year? | 全站归档时间线,按年月分组的整站文章目录 |
| 浏览 | get_random_content | categorySlug? | 随机返回一篇文章的 cid,可按分类 slug 限定 |
| 浏览 | list_travels | — | 旅行足迹地图的全部地点(名称、经纬度)及关联游记 |
| 浏览 | list_pages | limit?: 1~50 | 列出独立页面(留言板、协议、关于等),返回前台 URL |
| 导航 | list_categories | — | 列出全部分类及每分类下的文章数 |
| 导航 | list_tags | — | 列出全部标签及每标签下的文章数 |
| 导航 | list_category_articles | slug: string、limit? | 分类下文章列表 |
| 导航 | list_tag_articles | slug: string、limit? | 标签下文章列表 |
| 导航 | get_related_contents | cid: number、limit?: 1~20 | 相关文章,按共享标签数倒序 |
| 互动 | get_recent_comments | limit?: 1~50 | 全站最新已审核评论,含文章标题与链接 |
| 互动 | get_comments | cid: number、limit?: 1~100 | 单篇文章已审核评论 |
| 站圈 | list_friend_links | — | 已启用且已审核的友情链接 |
| 演进 | list_changelogs | limit?: 1~50 | 公开更新日志(按时间倒序) |
| 自述 | get_site_info | — | 站点名、URL、描述、备案号(来自 informations 表) |
所有检索工具的返回字段均经过公开接口白名单筛选:标题、描述、slug、文章链接、发布时间、正文纯文本与作者昵称可下发;ip、agent、uid、status 等内部字段一概不下发(get_content 返回的 type 是 post/page 语义标记,非内部列)。凡返回里带站内链接的工具,顶层都附 site_url(后台设置的站点地址,已剥尾斜杠);链接字段本身是相对路径,与 site_url 拼接即得绝对地址。
工具完整入参与返回
下表列出每个工具完整可选入参(默认值列说明客户端不传时使用什么)与关键返回字段。运维工具的 token 字段必须带,调用方自己持有 MCP_OPS_TOKEN 的值;本节示例均假定该变量已配置。
内容工具(19 个,全部只读)
| 工具 | 入参(完整) | 关键返回 |
|---|---|---|
search_content | q(必填)、limit(默认 10,1~20)、offset(默认 0,配合 has_more 翻页)、categorySlug?、tagSlug?、days?(1~3650,限定时间窗) | 顶层 site_url、q、offset、has_more(多查一条探测得出);命中数组含 cid、title、desc、slug、url、published_at、comment_num、like_num,正文命中附 match_excerpt(命中词前 40 后 80 字符) |
get_content | cid / slug 二选一(都传报 cid_slug_exclusive)、type(post 默认 / page,独立页面只能 slug 直查)、raw?(默认 false,true 返回原始 Markdown 字段名为 markdown 而非 plain_text)、offset(默认 0,长文续读入口)、maxChars(默认 8192,256~32768) | 顶层 site_url;条目含 cid、type、title、desc、url、author、published_at、updated_at、word_count(全文长度)、reading_time_min、comment_num、like_num、categories、tags、offset、returned_chars、has_more 与 next_offset(has_more 为真时),plain_text(或 markdown)截断时附 用 offset=N 续读 提示 |
list_content_images | cid(必填)、limit(默认 50,1~100,封面优先) | 顶层 site_url;条目含 url 与 source(cover/attachment),封面附 desc、width、height,附件另带 aid、name、format;文章不存在报 article_not_found |
list_recent_contents | limit(默认 10)、categorySlug?、days? | 顶层 site_url;文章数组含 comment_num、like_num |
get_popular_contents | sort(likes 默认 / comments)、limit(默认 10,1~20) | 顶层 site_url 与 sort;条目含 like_num、comment_num、url;只列真有热度的文章(likes 口径要求有点赞,comments 口径要求 comment_num > 0) |
list_archives | year?(只看某一年,UTC) | 顶层 site_url 与 total;groups 按 UTC 年月分组,每组含 year、month 与 items(cid、title、url),一次调用拿整站目录 |
list_categories | — | 顶层 site_url;分类数组含 content_count 与 latest_article_at(ISO 字符串或 null) |
list_tags | sort?(name 默认 / count 按文章数降序) | 顶层 site_url;标签数组含 content_count |
list_pages | limit(默认 20) | 顶层 site_url;独立页面数组含 slug、title、url、updated_at |
get_random_content | categorySlug? | 直接返回完整条目 {site_url, cid, title, desc, url, note},不必再调一次 get_content |
list_travels | — | 顶层 site_url;地点数组含 id、name、desc、cover、longitude、latitude 与 articles(关联游记,无分类或无 slug 的关联会被过滤以免死链) |
list_category_articles | slug(必填)、limit(默认 20) | 顶层 site_url;含 comment_num、like_num |
list_tag_articles | slug(必填)、limit(默认 20) | 同上 |
get_related_contents | cid(必填)、limit(默认 5) | 顶层 site_url;含 shared_tags(计数)与 shared_tag_names(标签名数组) |
get_recent_comments | limit(默认 20) | 顶层 site_url;评论为回复时附 parent_coid 与 parent_author(无名父评论时为 null) |
get_comments | cid(必填)、limit(默认 20)、order?(desc 默认 / asc) | 返回 total 总数 + items |
list_friend_links | — | 仅含 name、desc、url、avatar |
list_changelogs | limit(默认 10) | entries 数组由 changelog 的 content JSON 字段解析而来,解析失败兜底为 [] |
get_site_info | — | 站点基础信息 + stats 子对象(articles、comments、categories、tags、pages、friend_links、latest_publish_at、first_publish_at) |
运维工具(5 个;4 只读 + 1 写)
| 工具 | 入参 | 关键返回 |
|---|---|---|
get_system_status | token(必填) | time、node、node_env、platform、inDocker、buildHash、uptimeSec、heapUsedMB、rssMB、database(含 latencyMs 与版本)、redis(含 latencyMs、status、version,未配置时 configured: false) |
get_recent_logs | token、category(默认 app)、lines(默认 50,1~200)、level(默认 all,info/warn/error 可选)、keyword?(按子串过滤,最长 100 字符)、date?(YYYY-MM-DD 格式,默认今天) | category、date、shards、totalLines、returnedLines、lines(每行 ≤4000 字符,总量 ≤256 KB 兜底) |
get_content_stats | token | articlesByStatus、commentsByStatus、pagesPublished、links、attachments、subscribes、likes(total/last30days)、commentsLast24h、scheduledTotal、latestPublished、scheduledPublishQueue |
get_cache_info | token | Redis status、pingLatencyMs、dbSize、usedMemory、peakMemory、keyspace(hits/misses/hitRatePercent)、keyCounts(按 nitro:routes、search、custom、error:notify、rl:mcp:post 前缀分组计数) |
clear_cache | token、target(pages/search/footprint/rl/keyword)、keyword?(仅 target=keyword 时需要,通配符会被剥除)、confirm(必须显式 true 才执行) | target、cleared(整组清时为 -1)、note(rl 模式附带"误伤的 429 立即解除"说明) |
典型调用样例
下面给出几个真实使用场景,调用方按 JSON-RPC 工具调用协议发请求即可。所有示例均假定 MCP_OPS_TOKEN 已配置。
按分类与时间窗搜索近 30 天的文章:
JSON{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_content",
"arguments": { "q": "Nuxt", "categorySlug": "tech", "days": 30, "limit": 10 }
}
}取单篇文章的原始 Markdown(含字数、阅读时长、分类标签):
JSON{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_content",
"arguments": { "cid": 42, "raw": true }
}
}响应里 has_more 为真时按 next_offset 续读长文,读到的片段按 offset 顺序拼接即还原全文;独立页面把 cid 换成 slug 并配 type: "page":
JSON{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_content",
"arguments": { "cid": 42, "offset": 8192, "maxChars": 8192 }
}
}排查「改了内容前台不刷新」:先看缓存键空间命中率与各前缀键数,定位哪类缓存数量异常,再决定清哪一组。
JSON{ "params": { "name": "get_cache_info", "arguments": { "token": "REPLACE_WITH_MCP_OPS_TOKEN" } } }命中 keyspace.hitRatePercent 长期低于 50% 时考虑提高缓存 TTL 或扩大缓存容量;keyCounts.rl:mcp:post 飙高表示有 IP 在试探 MCP 端点。
误伤一个 IP 永久 429 时,清限流计数键即可立即解除(不再等窗口重置):
JSON{
"params": {
"name": "clear_cache",
"arguments": { "token": "REPLACE_WITH_MCP_OPS_TOKEN", "target": "rl", "confirm": true }
}
}读访问日志末尾 20 行、只看 ERROR、并过滤某个 cid 的报错:
JSON{
"params": {
"name": "get_recent_logs",
"arguments": { "token": "REPLACE_WITH_MCP_OPS_TOKEN", "category": "access", "lines": 20, "level": "error", "keyword": "cid=42" }
}
}降级到本月初看统计,定位近期内容走势:
JSON{
"params": {
"name": "get_content_stats",
"arguments": { "token": "REPLACE_WITH_MCP_OPS_TOKEN" }
}
}调用结果中的 scheduledPublishQueue 若非空说明有定时发布待执行;commentsLast24h 为 0 且 likes.last30days 持续为 0 时再去翻 access 日志查访问量是否同样跌零,定位站点侧还是流量侧问题。
运维工具组
在运行环境配置 MCP_OPS_TOKEN 后,端点会额外注册五个运维工具,供 AI Agent 直接排查线上问题;变量留空则整组不注册,客户端只能看到十九个检索工具。令牌每次请求都从环境变量现读,生产环境补配后无须重新构建。配置后调用运维工具必须携带 token 入参(即 MCP_OPS_TOKEN 的值),校验不符时返回 invalid_ops_token:
| 工具 | 入参 | 用途 |
|---|---|---|
get_system_status | — | 系统健康快照:运行时长与内存、构建哈希、是否容器、数据库与 Redis 的连通性和延迟 |
get_recent_logs | category、lines?: 1~200、level?: all/info/warn/error、date?: YYYY-MM-DD | 读结构化日志(logs/<类别>/<日期>.log,开启大小切分后自动合并全天分片)末尾若干行并可按级别过滤;类别限 access、audit、auth、external、cache、ratelimit、cron、monitor、app 九个白名单值,单行超 4000 字符截断 |
get_content_stats | — | 内容数据概览:文章与评论按状态分布、已发布页面、友链、附件、订阅数、最近发布一篇与定时发布队列 |
get_cache_info | — | Redis 缓存诊断:连通性与延迟、键总数、内存占用、按前缀统计键量(ISR 页面缓存、搜索、自定义、错误通知去重、MCP 限流) |
clear_cache | target: pages/search/footprint/keyword、keyword?、confirm: true | 清 Redis 缓存,清后自动重建;pages 清整组 ISR 页面缓存,keyword 按键名子串清理,子串中的通配符会被剥除 |
表中入参不含各工具必带的 token。前四个运维工具仍是只读,clear_cache 是全部二十四个工具中唯一的写操作。除令牌外还必须显式传 confirm: true 才执行。
限速
POST /mcp 自带按 IP 限速:单 IP 每分钟最多 60 次,超额返回 429 与 Retry-After(秒)。AI Agent 一次对话通常只有 1 至 5 次工具调用,60/分钟已很宽松;Redis 不可用时回落内存计数,重启清零。限速校验异常一律 fail-open,不阻断业务。
客户端接入
支持 MCP 的客户端只需配置端点 URL(如 https://imqi1.com/mcp)即可将工具纳入上下文:未配置 MCP_OPS_TOKEN 时可见十九个检索工具,配置后追加五个运维工具。在 Claude Desktop 中,把端点写入 MCP server 配置;在 Cursor 中作为自定义 MCP server 注册即可生效。开关关闭后客户端仍会看到 server,但调用工具时会收到 404。