feat(mcp): MCP server (search + publish) & fix empty English search index#365
feat(mcp): MCP server (search + publish) & fix empty English search index#365Crokily wants to merge 6 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f0c9156e8a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
收到,我看看 |
source.getPages() without a locale argument only returns default-locale
(zh) pages, so the static /search.en.json index has been empty since the
i18n split. Enumerate pages with getPages("en") and classify English
pages by the .en.md(x) file suffix in addition to lang frontmatter, so
translations missing lang: en are indexed and excluded from the zh shard.
Expose /api/mcp (Streamable HTTP, stateless) with two tools: - search: server-side Orama query over the existing zh/en Fumadocs index pipeline, public with Upstash rate limiting - publish: forwards to the backend POST /api/posts with the caller's satoken (Authorization: Bearer), returning the public post URL Credential verification is isolated in lib/mcp/auth.ts and only runs for publish calls; invalid tokens get a 401 challenge, auth-service outages a 503, and both backend fetches carry 10s timeouts. The route validates body size (1 MB), JSON syntax, and rejects JSON-RPC batches before the transport. /.well-known/oauth-protected-resource prepares the OAuth resource-server metadata; the authorization-server rollout plan for the backend team is documented in dev_docs/mcp_auth_upgrade.md.
f0c9156 to
7812941
Compare
/mcp (SSG, zh/en) lets users pick their MCP client and copy install commands with their satoken auto-filled client-side after login. Covers Claude Code, Codex CLI, OpenCode, Gemini CLI, Cursor, VS Code, the two web clients (search-only until OAuth), and Pi (via the companion skill/CLI since Pi has no MCP support). Command syntax per client was verified against current official docs. The token is read from localStorage only after mount and never appears in prerendered HTML. Also documents the sa-token sliding-renewal option for the backend as a near-term way to reduce 30-day token churn before OAuth.
Browser-hosted MCP clients preflight OPTIONS and need Access-Control-Allow-Origin on responses. Add an OPTIONS handler and a centralized CORS wrapper over GET/POST (streaming-safe), exposing WWW-Authenticate so browser clients can read the auth challenge. Wildcard origin is safe here: auth is bearer-header based, not cookies. Co-authored-by: chatgpt-codex-connector[bot] <chatgpt-codex-connector[bot]@users.noreply.github.com>
The /mcp page now builds install commands against the current origin after mount, so local dev and Vercel previews produce directly usable /api/mcp URLs while prerendered HTML keeps the production default.
|
该 PR 合并后,维护者需要进行一下 Registry 的登记,把我们的 MCP server( https://involutionhell.com/api/mcp )收录进官方 MCP Registry,收录后会自动同步到 GitHub MCP Registry,用户在各类 AI 客户端里搜 "involutionhell" 就能直接找到并接入。大概的做法就是:安装官方工具 mcp-publisher,进入 involutionhell-agent-tools 仓库的 docs/registry/ 目录(已备好 server.json 模板和详细 runbook),依次跑 mcp-publisher init、mcp-publisher login github(跳浏览器,用有 InvolutionHell org 身份的 GitHub 账号授权一次)、mcp-publisher publish,最后去 registry.modelcontextprotocol.io 搜 involutionhell 确认条目存在即可,全程不改代码、约十分钟。 |
这一步我会,我其他项目就登记过了,Gallama和其他awsome-project我都去注册一下,到时候把链接发Discord群里,大家去表态一下就可以增加成功率 |
用法
社区成员现在可以把本站作为 MCP Server 接入 Claude Code 或其他支持 Streamable HTTP 的 MCP 客户端。匿名连接后即可调用
search,按中文或英文索引搜索站内文章,并取得标题、摘要、相关正文片段和文章链接。登录连接后则可快速方便地发布文章。目前仅提供 search 和 publish 两个 tool,用于搜索文章,和发布文章
主要入口是站内的
/mcp页面。用户可以选择 Claude Code、Codex CLI、OpenCode、Gemini CLI、Cursor、VS Code、claude.ai 网页、ChatGPT 网页或 Pi,一键复制对应的安装命令或配置。登录后,页面生成的发布配置会自动填入当前账号的 satoken。需要发布内容时,必须使用附带 satoken 的安装方式,例如:
claude mcp add --transport http involutionhell https://involutionhell.com/api/mcp \ --header "Authorization: Bearer <你的satoken>"搜索不要求登录,配置了 token 的客户端也不会在搜索时访问鉴权后端。当前 satoken 最长有效 30 天,过期后需要重新登录,并在
/mcp页面重新复制配置。(该情况后续可通过后端配合优化,附带了一个文档用于后端团队参考:dev_docs/mcp_auth_upgrade.md)网页类 MCP 客户端目前还没有安全的 OAuth 授权和续期流程,因此现阶段只能可靠地使用匿名搜索。发布功能暂时面向能够由用户显式配置 token 的可信本地客户端。
安装后指明让 agent 调用 involutionhell mcp 即可工作:




配套的 Agent Skill 和 CLI
本 PR 还配套做了一个独立仓库:InvolutionHell/involutionhell-agent-tools。仓库中包含一个采用 Agent Skills 开放格式的
SKILL.md,可供支持该格式的多种 agent 使用;同时提供一个 Node CLI,包含search、publish和whoami,可以直接通过npx运行。做这部分的原因很直接。现在不少厂商正在把 CLI 作为比 MCP 更基础的 agent 接口。例如,Notion CLI 出现后基本取代原有的 Notion MCP,因此这次没有把本站能力只绑定在 MCP 上,而是顺便把 CLI 做成基础,实现了 MCP 同样的功能,并可用于未来拓展,再通过配套 Skill 告诉 agent 如何在 MCP、CLI、curl 和 GitHub PR 之间选择。
MCP 仍然是首选集成:客户端已经连接
involutionhellMCP Server 时,agent 可以直接调用结构化的search和publish工具,不需要管理额外命令。CLI 则适合能够使用 shell、但没有 MCP 连接的 agent (例如 Pi),也可以作为本地调试和轻量自动化的 fallback。两者共享搜索语义和发布 API,独立仓库不会复制一套 MCP Server。安装 Agent Skill:
没有 MCP 连接时,可以直接运行 CLI:
npx --yes github:InvolutionHell/involutionhell-agent-tools search "数组" --limit 3 npx --yes github:InvolutionHell/involutionhell-agent-tools publish post.md --dry-run搜索默认使用中文索引,也支持
--locale en、--json和--no-cache。发布读取 Markdown 文件及其 YAML frontmatter,命令行参数可以覆盖标题、摘要、标签和 slug。--dry-run只打印即将发送的 JSON,不访问网络,也不会打印 token。真实发布和
whoami只从环境变量读取凭证,避免 token 进入 shell history 或进程列表:Skill 还规定发布前必须先用中英文关键词和同义词查重,并根据内容形态选择发布轻量帖子,或通过 GitHub PR 收录进知识库。
CLI 和 Skill 也已通过测试:


开发思路
MCP Server 作为无状态的 Streamable HTTP 路由放在
/api/mcp,随现有 Next.js 站点部署。搜索没有另建内容库,而是复用站内搜索的 Fumadocs、pageToIndex和 Orama 索引管线,按中英文建立延迟加载的 shard,并沿用中文分词。发布则把 MCP 输入转换成现有后端格式,调用POST /api/posts,因此文章创建、slug 冲突和用户身份仍由原有后端处理。连接页是
/[locale]/mcp下的 SSG 页面。各客户端的命令语法依据 2026 年 7 月各自的官方文档逐项核对,而不是凭记忆编写。Gemini 配置使用httpUrl;VS Code 使用顶层servers和.vscode/mcp.json,避开已知的 header 问题;Codex 配置保存环境变量名,而不是直接保存 token;Pi 本身不支持 MCP,因此对应标签页指向配套的 Skill/CLI 仓库。satoken 只在客户端挂载后从 localStorage 读取,不会出现在预渲染 HTML 中。所有配置片段都由纯函数生成,并有单元测试覆盖。鉴权集中在
lib/mcp/auth.ts。只有publish调用会校验 bearer satoken,工具层只接收已经验证的身份和后端请求头。项目同时加入了/.well-known/oauth-protected-resource和后端升级文档,后续接入 OAuth 时可以替换 token 校验方式,不需要修改工具参数和发布流程。目前 satoken 过期后需要在
/mcp页面重新复制。文档向后端提出了 sa-token 基于活动自动续期的滑动续期方案,作为 OAuth 落地前降低 30 天 token 更新频率的低成本缓解。纯滑动续期的安全代价是,泄漏但持续活跃的 token 可能永不过期,因此建议同时设置绝对最长生命周期。根治方案仍是支持 refresh token 的 OAuth,具体方案见dev_docs/mcp_auth_upgrade.md。开发过程中还发现了一个原有站点问题:
source.getPages()不传语言时只返回中文页面,导致/search.en.json一直生成空索引。现在英文路由改用source.getPages("en"),并同时识别lang: en和.en.md、.en.mdx文件名,缺少语言 frontmatter 的英文页面也能进入正确索引。对抗性 review 发现,畸形 JSON 会让上游处理器挂到函数超时,因此路由现在会先读取并解析请求体:畸形 JSON 返回 400,JSON-RPC batch 直接拒绝,超过 1 MB 返回 413。工具字段加入长度和数量限制,鉴权与发布请求都设置 10 秒超时;缺失或无效 token 返回带 challenge 的 401,鉴权服务超时、网络失败或 5xx 返回 503,避免把服务故障误报成凭证失效。失败的索引构建也会清除缓存,允许后续请求重试。
当前 14 个测试文件中的 110 个测试全部通过。构建路由表新增
/api/mcp和/.well-known/oauth-protected-resource两个动态路由,以及/[locale]/mcpSSG 页面,没有改变既有路由的渲染模式。完整的 MCP 协议流程,包括 initialize、tools/list、中英文搜索、发布鉴权路径和畸形请求等异常用例,已经在本地完成端到端验证,并在两个 Vercel Preview 部署上实测了真实搜索调用。本 PR 当前涉及 33 个文件,共新增 3201 行、删除 6 行。其中测试目录新增 725 行,覆盖上述安全修复、英文索引修复和连接配置,并随 pre-commit 和 content-check CI 执行。另有两份共 278 行的 dev_docs 文档:
mcp_server.md是本功能的设计与运维说明,mcp_auth_upgrade.md是给后端团队的 OAuth 升级配合方案,供后续排期使用。