Skip to main content
Glama
README.md
# MCPs

MCPs 是一个面向 Agent 的远程能力服务与管理控制台。它通过 MCP Streamable HTTP 提供网络搜索、网页解析、本地知识库检索和 LLM 委托能力,不包含 STDIN/STDIO 传输。

> 项目状态:`0.1.0` 预发布版本。项目采用 MIT License;核心能力、管理控制台、首次配置引导、自动化测试与 Docker 单机部署已可用。完成下方安全发布清单前,仅建议用于本地开发或受控私有环境。当前 `package.json` 保持 `private: true`,不会发布到 npm。

项目代码采用 [MIT License](LICENSE)。第三方运行时依赖及再分发要求见
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md);品牌素材的来源记录见
[docs/brand-icon.md](docs/brand-icon.md)。

## 能力

- 网络搜索:Serper、Bocha,统一结果模型与供应商切换。
- 网页解析:静态抓取、可选 Playwright 动态渲染、Markdown、原始 HTML、文件链接、元数据、超长响应限制与截断标记。管理端可同时查看 Markdown 预览、Markdown 源码和原始 HTML;MCP 与内部 LLM 工具只传一次清洗后的 Markdown,不传原始 HTML。
- 知识库:文件系统作为事实源,SQLite FTS5 + BM25 本地索引;中文 NFKC 归一化与 bigram,不使用向量或 embedding。
- LLM:OpenAI Responses API、DeepSeek 与自定义 OpenAI-compatible Chat Completions 服务;管理端可配置 Provider、SK、Base URL、模型列表、默认思考级别、温度和输出上限;支持本地持久化多轮会话,以及网络搜索、网页解析、知识库检索和受限的知识库文档创建。
- 管理服务:首次登录提供三步配置引导;概览页集中展示能力可用性、最近调用样本、平均耗时、有效密钥和待处理事项;配置中心聚合真实生效状态、配置来源与生效方式;每项能力仍保留独立 UI 和 REST 测试入口。

## 产品流程

1. 首次访问时创建唯一管理员账号;注册入口随后永久关闭。
2. 首次配置引导依次确认访问地址与鉴权、可选搜索/LLM Provider,以及最终端点检查。进度保存在服务端,刷新后可继续。
3. 完成或跳过引导后默认进入“概览”,查看当前运行状态和最近最多 50 次 MCP 调用样本。
4. 需要修改配置时进入“配置中心”,或直接进入对应能力的配置与 Playground。

概览中的成功率和平均耗时是最近最多 50 次 MCP 网络调用的样本统计,不代表自然日全量或时间序列趋势。调用日志不包含管理后台操作,也不保存请求正文、响应正文或认证头。

## 架构

后端按 SOLID 原则拆分:

```text
src/server/
├── */domain 或 */ports       # 业务模型与抽象接口
├── */application             # 用例编排,不依赖 Web 框架或供应商 SDK
├── */infrastructure          # Serper、Bocha、Playwright、SQLite、模型 SDK 适配器
├── mcp                       # MCP 工具注册与 HTTP 传输入口
├── admin                     # 管理端应用服务
├── config                    # 环境配置
├── shared                    # HTTP、鉴权、错误与序列化
└── container.ts              # 唯一应用组合根
```

例如,搜索应用服务只依赖 `SearchProvider`,LLM 应用服务只依赖 `LlmProvider`;切换供应商不需要修改业务用例或路由。

## 本地启动

要求 Node.js 22+。

```bash
cp .env.example .env.local
npm install
npm run dev
```

默认仅监听 `127.0.0.1`,打开 [http://localhost:3000](http://localhost:3000)。启动器会先探测端口,如果默认端口已被占用则自动递增,并在终端打印实际访问地址;也可以显式运行 `npm run dev -- --port 3001`。只有明确需要局域网访问时才传入 `--hostname 0.0.0.0`,并应先配置凭据。首次访问需要创建唯一管理员并完成或跳过首次配置引导,后续只显示登录并默认进入概览。未配置第三方 API Key 时,知识库和静态网页解析仍可运行;搜索与 LLM 会在 UI 和 API 中显示为“待配置”。

`.env.example` 中的所有值均为已注释的占位符,不包含可直接使用的凭据或部署地址。复制后只取消注释需要的配置,并把每个 `<PLACEHOLDER>` 替换为真实值;未替换的占位符不会被应用读取。

## MCP 网络端点

| 端点             | 工具                                                                                           |
| ---------------- | ---------------------------------------------------------------------------------------------- |
| `/mcp`           | 全部工具                                                                                       |
| `/mcp/search`    | `web_search`                                                                                   |
| `/mcp/parse`     | `parse_webpage`                                                                                |
| `/mcp/knowledge` | `search_knowledge`、`list_knowledge_documents`、`read_knowledge_document`、`reindex_knowledge`、`write_knowledge_document` |
| `/mcp/llm`       | `delegate_llm_task`                                                                            |

这些端点使用 MCP Streamable HTTP,并支持 2026-07-28 per-request 协议与 2025 客户端的 stateless 兼容模式。部署时设置 `MCP_PUBLIC_BASE_URL` 为 HTTPS 公网地址。生产环境没有环境 Token、没有有效管理密钥且未强制鉴权时,只允许 loopback 本机匿名访问,远程请求会被拒绝。远程使用应配置 `MCP_API_TOKEN` 或在管理端创建 API 密钥;请求需携带:

`delegate_llm_task` 可在一次调用中推送增量进度。客户端需要在 `tools/call` 参数的 `_meta.progressToken` 提供任意字符串或数字;服务端随后通过标准 `notifications/progress` 返回递增的 `progress`,并将原始结构化事件放在 `params._meta["mcps.llm.event"]`。没有 `progressToken` 时,工具结果保持原有的一次性 `tools/call` 返回形式。

```http
Authorization: Bearer <token>
```

## 管理 API

管理 UI 和所有 `/api/admin/**` 接口使用管理员会话 Cookie 鉴权;MCP 网络端点继续独立使用 Bearer Token。

- `GET /api/auth/status`
- `POST /api/auth/bootstrap`
- `POST /api/auth/login`
- `POST /api/auth/logout`

本地开发时,空数据库只允许从 loopback 创建首个管理员;生产环境则始终要求至少 24 个字符的 `MCP_ADMIN_BOOTSTRAP_TOKEN`,包括从服务器本机初始化,以避免伪造 Host 请求绕过边界。刷新页面后输入该一次性令牌;管理员创建成功后注册入口永久关闭。登录按来源限制为 10 次/15 分钟并设 200 次/15 分钟的进程级总量限制,首次注册按来源限制为 5 次/小时并设 25 次/小时的总量限制;超限返回 `429` 与 `Retry-After`。多副本部署仍应在反向代理或共享存储层增加统一限流。

- `GET/PATCH/POST /api/admin/onboarding`
- `GET /api/admin/health`
- `GET /api/admin/capabilities`
- `POST /api/admin/search`
- `GET/PUT /api/admin/search/config`
- `POST /api/admin/parser`
- `POST /api/admin/llm`
- `GET /api/admin/llm/config`
- `PUT /api/admin/llm/config`
- `POST /api/admin/llm/models`
- `GET/POST /api/admin/llm/sessions`
- `GET/DELETE /api/admin/llm/sessions/:id`
- `GET /api/admin/knowledge/documents`
- `POST /api/admin/knowledge/directories`
- `GET /api/admin/knowledge/documents/:id`
- `GET /api/admin/knowledge/documents/:id/preview`
- `GET /api/admin/knowledge/search?q=...`
- `POST /api/admin/knowledge/index`
- `GET/POST /api/admin/api-keys`
- `DELETE /api/admin/api-keys/:id`
- `GET /api/admin/call-logs`
- `GET /api/admin/call-logs/:id`
- `GET/PUT /api/admin/system-settings`

`POST /api/admin/llm` 默认返回 JSON。请求头包含 `Accept: text/event-stream`,或请求体包含 `"stream": true` 时,会改为 SSE:依次发送 `start`、模型产生的 `phase`、`text_delta`、`tool_start`、`tool_end` 事件,最后发送包含会话 ID 的 `done`;失败时发送不含内部错误细节的 `error`。关闭客户端连接会取消正在进行的模型请求,取消的结果不会写入本地会话。

管理端可以创建和吊销 MCP API 密钥。密钥明文只在创建成功后显示一次,服务端仅保存 SHA-256 摘要;只要存在有效的管理密钥,MCP Bearer 鉴权就会自动启用。调用日志只记录端点、方法或工具、状态、耗时和密钥标识等元数据,不保存请求正文、响应正文或认证头,并按“系统设置”中的保留天数和最大条数自动清理。公网地址、鉴权开关与日志保留策略保存后即时生效。

LLM Provider 的 SK 可以来自服务器环境,也可以通过管理 UI 保存。UI 保存的配置位于 `MCP_DATA_DIR/llm-settings.json`,文件权限为 `0600`,接口只返回配置状态和末四位提示,不返回完整密钥。UI 保存的配置优先于启动环境变量。

进入“LLM 服务”默认显示“模型配置”:左栏选择 Provider,右栏展示其模型列表。Provider 连接配置负责密钥和模型列表刷新;每个模型的“高级配置”独立保存上下文窗口、最大输出、思考级别、温度及工具预算。保存高级配置不会自动切换默认模型,需另点“设为默认”。配置按 Provider 与模型名共同隔离,管理页和 MCP 调用均会读取对应模型的参数,显式请求参数优先。旧版默认参数会保留;未单独配置的其他模型使用兼容预算,不代表自动识别的模型规格。

### 配置与 Playground

配置中心并行读取能力状态、系统设置与 API 密钥,只展示服务端当前已生效值,并按“基础访问、能力连接、运维策略”聚合配置来源、生效方式和待处理入口。网络搜索、网页解析、知识库与 LLM 仍进入各自的配置主区域,测试通过右侧 Playground 按需展开。宽屏可同时修改配置和查看测试,支持拖动分隔线或使用左右方向键调宽;窄屏自动全屏,桌面也可手动全屏。收起测试面板不会卸载其内容,因此输入、结果与正在进行的 LLM 输出会保留;返回配置中心或刷新页面仍会结束当前工作区,已持久化的会话可从历史重新打开。

- 搜索:管理已保存的默认 Provider 和密钥;测试可以临时选择其他已配置 Provider,不会自动保存配置草稿。
- 解析:主区展示运行环境配置;URL、动态渲染开关、字符上限与超时属于本次测试参数。原始 HTML、Markdown 与元数据均在测试面板查看。
- 知识库:默认展示运行配置与索引操作,文件树、文档阅读及 HTML 新窗口预览保留在主工作区;检索测试不会写入文档或自动重建索引。
- LLM:每个模型的“测试模型”显式指定 Provider 和模型,不需要修改全局默认;会话历史位于测试面板内。

配置中心中的文字主按钮会进入对应配置主区;搜索和 LLM 的管理页保存后立即用于新请求,解析和知识库的环境级配置仍通过服务运行环境管理,不会把临时测试参数当作已保存配置。配置中心不提供跨存储域的“保存全部”,也不再用仅存在于前端的新增、启停或删除状态模拟入库。

搜索 Provider 的 API Key 可以在“网络搜索”页面后台配置,保存到 `MCP_DATA_DIR/search-settings.json`(权限 `0600`),只返回末四位提示。留空保存会保留已有 Key;点击“清除”后保存可以移除对应 Provider。环境变量仍可作为首次启动时的默认值。

LLM 会话与消息保存在 `MCP_DATA_DIR/llm-conversations.sqlite`。系统回放最近 120,000 字符的 user/assistant 历史,并持久化 Provider、模型、Token 用量及按时间顺序的回答、工具调用和预算收尾事件。OpenAI Responses API 使用 `store: false`,本地 SQLite 是会话事实源。

在 LLM 配置中按所用模型填写上下文窗口(兼容默认值为 128,000 Tokens,不是自动识别的模型规格)。系统估算当前请求的系统提示、历史、任务、工具定义与结果;达到窗口约 80% 时停止工具调用,并只再请求一次无工具的最终回答,要求模型基于已有信息给出结论、标明未知事项,不压缩后继续探索。系统还会预留最大输出 Tokens,因此输出预留较大时可能提前收尾。Token 估算并非供应商精确计数;过大的工具结果可能被截断以给最终回答腾出空间。

不会额外调用模型做摘要压缩。若收尾请求仍放不下,系统仅为这次最终回答整理已完成的工具资料、标注截断并减少旧历史,不再恢复工具探索;系统提示和当前用户任务不被截断。若它们本身已经超出可用窗口,会返回明确的参数错误。

搜索、解析和知识检索是只读工具;用户明确要求保存时,模型可通过 `write_knowledge_document` 在知识库根目录内创建文档(包括 HTML),成功后自动索引,返回路径、标题和行数等摘要,不回显正文。不允许任意文件写入、路径穿越、符号链接或覆盖已有不同内容。管理页和 MCP 共用同一写入服务;生产 MCP 必须开启鉴权。工具头显示搜索关键词、网页标题等摘要;每次调用只显示一张卡片,结果返回后更新该卡片,点击查看参数与结果,不额外插入结果行。代码块和文件正文默认折叠,按需查看详情,原始消息与时间线仍完整保存。

高级设置中的工具预算默认为 **8 轮 / 24 次**(可分别配置 1–32 轮、1–128 次);一次模型回复可含多个工具调用,失败调用也计数。这两项是防止循环的兜底,通常保留默认值即可。达到任一工具预算也进入最终回答,而不是直接抛出“工具调用已达到安全上限”。有保存要求时模型会被提示优先预留写入机会;若未得到写入成功结果,不应宣称文件已保存。

## 知识库索引

把文件放入 `MCP_KNOWLEDGE_DIR`,然后登录管理控制台,在“知识库”页面执行增量索引或完整重建。索引接口属于 `/api/admin/**`,必须携带有效的管理员会话 Cookie;不能匿名调用。

索引流程:文件扫描 → `path/size/mtime` 快速判断 → 必要时 SHA-256 内容判断 → Markdown/HTML/文本提取 → 按标题约 900 token 分块并保留约 100 token 重叠 → Latin token 与中文 bigram → FTS5 → 标题/章节/正文加权 BM25 排序。

小于约 1500 token 的文档保留为一个块。HTML 的索引文本会移除脚本和样式;预览 API 返回原始源码,前端不会用 `dangerouslySetInnerHTML` 直接执行它。

网页解析的 `maxChars` 只限制给 LLM 的 Markdown 长度,不会因为这个参数截断管理端的原始 HTML。原始 HTML 仍受抓取器的响应体上限保护,超出时通过 `truncation.responseBody` 标记。

Markdown 清洗使用通用规则:正文优先从 `article`、`main`、`articleBody`、`markdown-body` 等内容节点提取;移除脚本、样式、导航、页脚、Cookie、分享、推荐和版权模块;图片替换为 `【图片:说明】` 占位符;超过 200 个字符的链接只保留可读文本,不把长地址送入 LLM。HTML 解析和 Markdown 转换分别由 Cheerio 与 Turndown 完成,站点降噪规则由 MCPs 自己维护。

## 动态网页解析

静态解析无需浏览器。动态 JavaScript 页面需配置其中一个选项:

- `PARSER_BROWSER_CDP_URL`:连接部署环境中的远程 Chromium。
- `PARSER_BROWSER_EXECUTABLE_PATH`:启动服务器上的 Chromium 可执行文件。

解析器拒绝回环、私网、链路本地及保留地址,并对每次重定向重新校验,降低 SSRF 风险。

## 部署

```bash
cp .env.example .env
mkdir -p knowledge
docker compose up --build
```

Compose 将 SQLite 数据持久化到 volume,并把 `./knowledge` 可写挂载为知识文件目录,以支持模型创建文档。宿主机目录需允许容器用户 UID/GID `1001:1001` 写入;若保留 `:ro` 挂载则只能检索,创建文档会失败。生产环境应配置 HTTPS 反向代理、`MCP_PUBLIC_BASE_URL` 和强随机 `MCP_API_TOKEN`(或在管理端创建 API 密钥);需要从远程完成首次初始化时还需临时配置 `MCP_ADMIN_BOOTSTRAP_TOKEN`。

Compose 固定容器内 `MCP_DATA_DIR=/data` 与 `MCP_KNOWLEDGE_DIR=/knowledge`:`.env` 中的路径不会覆盖持久化卷和知识库挂载。启动前取消注释所需配置并替换全部占位符;不要把 `.env` 提交到仓库。

Compose 默认把宿主机端口绑定到 `127.0.0.1:3000`,适合由同机 HTTPS 反向代理转发。若要直接绑定所有网卡,必须显式修改端口映射,并在暴露前完成管理员初始化和 MCP 凭据配置;反向代理还应覆盖来源转发头并拒绝不匹配的 Host。

建议的生产启动与检查流程:

```bash
docker compose config
docker compose up --build -d
docker compose ps
curl --fail http://127.0.0.1:3000/api/auth/status
```

`/api/auth/status` 可用于容器健康检查,不返回管理员或密钥内容;管理健康接口需要管理员会话。镜像以非 root 用户运行,命名卷 `mcps-data` 保存 SQLite、认证、密钥摘要和运行配置;升级或迁移前请先备份该卷。当前 SQLite 持久化是单实例设计,不要让多个应用副本同时写入同一个数据卷。

静态网页解析不需要额外服务。若要解析依赖 JavaScript 的网页,请在 `.env` 配置可访问的 `PARSER_BROWSER_CDP_URL`;默认镜像不内置 Chromium。公网部署应由支持 HTTPS 与流式响应的反向代理转发到容器,且不要缓冲 MCP 的流式响应。

## 运行数据

| 路径 | 内容 |
| --- | --- |
| `MCP_DATA_DIR/auth.sqlite` | 唯一管理员、会话和首次配置状态 |
| `MCP_DATA_DIR/system-settings.sqlite` | 公网地址、鉴权开关和日志保留策略 |
| `MCP_DATA_DIR/api-keys.sqlite` | 管理型 MCP API 密钥摘要与使用状态 |
| `MCP_DATA_DIR/call-logs.sqlite` | MCP 调用元数据 |
| `MCP_DATA_DIR/knowledge-index.sqlite` | 知识库索引与文档元数据 |
| `MCP_DATA_DIR/llm-conversations.sqlite` | 本地 LLM 会话和消息 |
| `MCP_DATA_DIR/search-settings.json` | 搜索 Provider 配置 |
| `MCP_DATA_DIR/llm-settings.json` | LLM Provider、模型和预算配置 |
| `MCP_KNOWLEDGE_DIR` | 知识库原始文档,事实源 |

管理 UI 保存的系统、搜索和 LLM 设置优先于环境变量提供的启动默认值。`.data/`、`.env*`(除 `.env.example`)和 TypeScript/Next.js 构建产物均已在 Git 中忽略。

## 安全与数据边界

- 管理后台使用 `HttpOnly`、`SameSite=Lax` 的管理员会话 Cookie;生产环境 Cookie 同时启用 `Secure`。
- MCP 网络端点与管理后台使用独立鉴权。管理员 Cookie 不能代替 MCP Bearer Token。
- 空数据库首次管理员在本地开发中仅允许 loopback 创建;生产环境始终必须配置 `MCP_ADMIN_BOOTSTRAP_TOKEN`。管理员存在后注册入口永久关闭。
- 生产环境没有任何 MCP 凭据时,匿名 MCP 请求只允许来自 loopback;公网使用应配置强随机 `MCP_API_TOKEN` 或通过管理后台创建有效 API 密钥。
- 登录与首次注册同时执行来源级和进程级总量限流;多副本部署还需由网关提供共享限流。
- 管理端创建的 API 密钥仅保存 SHA-256 摘要;原始密钥只显示一次。
- 搜索与 LLM Provider 密钥保存在权限为 `0600` 的配置文件中,接口只返回配置状态和末四位提示。
- 网页解析会拒绝回环、私网、链路本地和保留地址,并在重定向后重新检查目标。
- 数据与知识目录统一为 `0700`;SQLite 主文件、WAL/SHM、Provider JSON 和新写入的知识文档统一为 `0600`。备份、日志收集和问题反馈时不要上传这些文件。

## 开源发布清单

当前代码可以作为候选公开仓库继续准备,但在完成以下事项前不建议标记为正式开源版本:

- [x] 使用 MIT License,并在仓库根目录提交 `LICENSE`。
- [x] `.env.example` 全部使用已注释占位符,不包含可通过生产校验的示例凭据。
- [x] 本地开发首次管理员仅限 loopback 创建;生产环境始终需要显式引导令牌,创建后注册入口永久关闭。
- [x] 生产环境未配置 MCP 凭据时仅允许 loopback 匿名访问,远程请求默认拒绝。
- [x] 数据与知识目录、SQLite/JSON、WAL/SHM 和新知识文档权限已统一收紧并有回归测试。
- [ ] 添加 `SECURITY.md`,说明私下报告漏洞的渠道和支持版本。
- [ ] 添加 `CONTRIBUTING.md`;若接受外部社区参与,再补充行为准则和 Issue/PR 模板。
- [ ] 配置 GitHub Actions,至少在 Node.js 22 上执行 `npm ci` 和 `npm run check`。
- [x] 为登录和首次注册配置来源级与进程级应用限流,超限返回 `429` 与 `Retry-After`。
- [ ] 补充 CSP、`frame-ancestors`、`X-Content-Type-Options`、Referrer Policy 等响应头策略。
- [ ] 发布前从干净 clone 验证本地启动、Docker 构建、首次配置和 MCP 客户端接入。
- [ ] 明确 MCPs 名称与 Logo 是随 MIT 授权还是保留为项目标识;未使用的 Create Next App 模板素材已移除。
- [x] 记录直接生产依赖与传递许可证摘要、Playwright NOTICE 和品牌素材生成来源;完整 Git 历史未发现真实密钥。
- [ ] 为第一个公开版本补充变更记录、支持范围和已知限制。

满足许可证要求后仓库才具备明确的开源许可;许可证与四项高优先级安全项完成后,再评估公开预览。其余项目决定首个正式版本是否达到可维护、可接受外部贡献的标准。

## 验证

```bash
npm run check
```

`npm run check` 会依次执行 ESLint、TypeScript、Vitest 和 Next.js 生产构建。提交前应完整运行;修改单个模块时可以先运行对应测试文件,再执行全量检查。

Maintenance

ActivityMaintained
ResponsivenessNo issues