内容
# serve-mcp
本地、由 MCP 控制的**制品架**。您的 AI 代理发布它们生成的 HTML、Markdown、文件夹、CSV 和 JSON;您获得稳定、安全的浏览器 URL 来查看它们。适用于同时运行多个代理的机器:每个代理都有自己的 MCP 进程,它们都发布到同一个共享架上。


## 快速入门
```bash
npm install -g @saidmukhamad/serve-mcp
```
将其添加到您运行的任意代理中 — 它们都发布到**同一个架子上**(第一个进程启动 HTTP 服务器,其余进程找到它):
```bash
# Claude Code
claude mcp add serve-mcp -- npx -y @saidmukhamad/serve-mcp mcp
# Codex
codex mcp add serve-mcp -- npx -y @saidmukhamad/serve-mcp mcp
# Gemini CLI
gemini mcp add serve-mcp npx -y @saidmukhamad/serve-mcp mcp
```
任何其他支持 MCP 的内容(Cursor、Windsurf、...)— 通常的 `mcpServers` 块:
```json
{
"mcpServers": {
"serve-mcp": { "command": "npx", "args": ["-y", "@saidmukhamad/serve-mcp", "mcp"] }
}
}
```
就是这样。要求任意代理发布某些内容并打开它返回的 URL — 一个适用于所有内容的画廊,无论谁创建了它。
### 永久设置
一台始终在线的服务器,机器上的每个代理和人都使用它:
```bash
serve-mcp config host 0.0.0.0 # 可通过您的 tailnet/LAN 访问(如果仅限本地访问,请跳过)
serve-mcp config port 7331 # 固定端口,稳定的 URL
serve-mcp service install # launchd / systemd --user / Task Scheduler
loginctl enable-linger $USER # 仅限 Linux:在注销后保持运行
```
如果没有这个架子,仍然可以工作 — 它只是随着您的 MCP 会话的生命周期而存在(详见 [生命周期](#生命周期-谁让架子保持运行))。
### 主动发布
服务器的 MCP 指令已经告诉代理主动发布可查看的输出。对于 Claude Code 中的最强提示,安装捆绑的技能 — 只要代理生成可查看的内容,它就会自动触发:
```bash
mkdir -p ~/.claude/skills/publish-artifact
curl -fsSL https://raw.githubusercontent.com/saidmukhamad/serve-mcp/main/skills/publish-artifact/SKILL.md \
-o ~/.claude/skills/publish-artifact/SKILL.md
```
## 工作原理
```txt
MCP = 控制平面 (artifact_publish, artifact_list, resources)
HTTP = 人类预览平面 (gallery, /p/:slug, sandboxed previews)
Registry = SQLite (稳定发布 -> 修订版)
Store = live | snapshot (路径/文件夹默认实时提供;live:false 冻结副本)
```
## 工具
### `artifact_publish`
```jsonc
{
"source": { "type": "path", "path": "./report.md" }, // 或 content / 文件夹
"title": "训练运行解释",
"slug": "training-run-explanation", // 稳定 /p/<slug>;如果省略则生成
"updateExisting": true, // 添加修订版;false = 冲突于现有 slug
"live": true, // 默认路径/文件夹:编辑时刷新显示;false = 冻结快照
"tags": ["ml", "report"],
"renderer": { "options": { "allowScripts": false } } // 除非请求,否则脚本保持关闭
}
```
返回预览 URL、原始 URL、MCP `resource_link` 和结构化 `artifact`/`publication` 对象。发布是稳定的插槽;每次发布都是 `/p/:slug/r/:artifactId` 下的不可变修订版。
### `artifact_list`
按 `query`、`tags`、`kind` 过滤;使用 `cursor` 分页;按 `createdAt | updatedAt | title` 排序。
### `artifact_delete`
通过 `slug` 删除发布 — 所有修订版和存储的文件都随之删除。不可逆。
### 资源
只有 `registry://publications`(画廊上的所有内容的 JSON 列表)在 `resources/list` 中出现,因此主机 UI 保持干净,无论存在多少发布。其他两个在直接读取时解析(工具结果链接到它们):`publication://<slug>`(紧凑 JSON)和 `artifact://<id>`(修订版的原始源)。
## 多代理端口发现
N 个代理,一个架子,无需协调。启动的第一个 `serve-mcp` 绑定一个端口( `--port`/`SERVE_MCP_PORT`,否则为临时端口)并在 `<dataDir>/server.json` 中记录其可访问 URL;其他 `serve-mcp` 进程 — 代理、CLI — 找到该记录,检查 pid 是否活动,然后发布到正在运行的架子上,而不是启动自己的架子。
## 渲染和安全性
路径和文件夹源默认**实时**提供 — 架子直接从源读取每次请求,因此编辑时刷新显示(如果源移动,页面会损坏)。发布时使用 `live: false`(CLI: `--snapshot`)将不可变的副本冻结到存储( `~/.local/share/serve-mcp`)中;内联 `content` 源始终存储。Markdown/MDX 通过 [Sätteri](https://satteri.bruits.org) 渲染(GFM、frontmatter、实时 `mermaid` 图表);JSON 漂亮打印;CSV 变成表格;文件夹作为静态站点提供。
服务器绑定到 `127.0.0.1`,除非您选择 `0.0.0.0`,并且没有身份验证 — 仅将它暴露给您信任的网络(Tailscale tailnet 合格;开放互联网不合格)。使用 `SERVE_MCP_ALLOWED_ROOTS=/path/a:/path/b` 限制路径发布。
### 文件夹导航
文件夹表现类似于经典文件服务器:每个目录提供自己的 `index.html` / `index.md` / `README.md`(或传递 `entrypoint`),目录重定向到 `dir/`,因此相对和 `../` 链接解析,而没有索引的目录获得可浏览的列表,带有 `../` 条目。文件夹内 Markdown/CSV/JSON 实时渲染,任何文件都可以通过 `?raw` 下载。
### 源信息捕获
每次发布都会记录其来源 — 源目录加上 git 分支、远程和提交 — 直接从 `.git` 文件读取(无需 `git` 子进程,即使没有安装 git 也能工作)。这显示在画廊卡片和预览子栏中。
## HTTP 路由
```txt
GET / 画廊(搜索、固定、最近)
GET /p/:slug 最新修订版,已渲染
GET /p/:slug/r/:artifactId 特定修订版
GET /raw/:artifactId 原始源
GET /meta/:artifactId 工件元数据 JSON
GET /api/publications JSON 列表(查询、光标、限制)
DELETE /api/publications/:slug 删除发布及其所有修订版
```
## 生命周期:谁让架子保持运行
一个架子运行在三种模式之一:
1. **会话管理**(默认)— 机器上的第一个 MCP 会话作为副作用成为架子,并随着该会话的生命周期而存在;下一个会话接管。零设置,但当没有会话打开时,架子会有间隙,除非您设置了固定端口,否则 URL 会更改。
2. **前台** — `serve-mcp serve` 在终端中。Ctrl-C 终止它,没有任何东西重启它。
3. **服务管理**(推荐)— 操作系统监督它:在登录时启动,在崩溃时重启,独立于任何会话或终端。
`serve-mcp` 显示正在运行的模式;`serve-mcp restart`(别名 `apply`)重启它 — 通过 supervisor 重启服务时,否则通过 pid。服务模式是顶部的[永久设置](#永久设置)。使用 `serve-mcp service start|stop|restart|status|logs|uninstall` 管理它。所有操作都是用户级别的 — 无需 root/admin:
- **macOS** — launchd 代理(`io.github.saidmukhamad.serve-mcp` in `~/Library/LaunchAgents`),KeepAlive 监督,日志在 `<dataDir>/serve.log` 中。
- **Linux** — systemd 用户单元,`Restart=on-failure`,日志在 journal 中。为了在注销后生存:`loginctl enable-linger $USER`。在 WSL 上,systemd 用户服务通常不可用 — 改用 tmux 运行 `serve-mcp serve`。
- **Windows** — Task Scheduler 任务从 XML 注册(无需管理员):在登录时通过 `wscript` 以隐藏方式启动,在失败时重启,日志在 `<dataDir>\serve.log` 中。
服务固定当前运行时和包路径(安装时显示)— 重新运行 `service install` 以升级两者。
## Tailscale / LAN 访问
要从其他机器访问架子,请将 `host` 设置为 `0.0.0.0`(上面的永久设置已执行此操作)。然后,广告 URL 会自动选择最佳的可访问名称:MagicDNS 名称(通过 Quad100 通过反向 DNS 学习,并通过系统解析器验证,因此仅在对等方可以实际解析时使用)→ Tailscale IP(`100.64.0.0/10`)→第一个 LAN 地址。Tailnet 检测不需要 Tailscale 工具 — 它依赖于接口的 CGNAT 地址和 Tailscale 的 ULA 前缀。
## CLI
```bash
serve-mcp ./report.md # 发布任意内容,获取其 URL
serve-mcp . # 提供此目录,实时
serve-mcp # 状态:正在运行的架子 + 其上的内容
serve-mcp config host 0.0.0.0 # 设置配置(host、port、baseUrl)
serve-mcp serve # 仅 HTTP 架子
serve-mcp publish ./report.md --title "报告" # 长选项,连同选项
serve-mcp list # (还发现正在运行的架子)
```
## 配置
`serve-mcp config` 显示它,`serve-mcp config <key> <value>` 设置它(空值取消)。存储在 `<dataDir>/config.json` 中(默认 `~/.local/share/serve-mcp/config.json`),所有内容都是可选的:
```json
{
"host": "0.0.0.0",
"port": 7331,
"baseUrl": "http://my-machine.tailnet.ts.net:7331",
"allowedRoots": ["~/projects", "/srv/artifacts"]
}
```
- `host` — 绑定主机,默认 `127.0.0.1`
- `port` — 固定端口;省略临时端口 + 通过 `server.json` 发现
- `baseUrl` — 广告 URL 覆盖(例如 MagicDNS 名称)
- `allowedRoots` — 限制 `path`/`folder` 发布可从何处读取(默认:任何可读位置)
环境变量(`SERVE_MCP_HOST`、`SERVE_MCP_PORT`、`SERVE_MCP_BASE_URL`、`SERVE_MCP_DATA_DIR`、`SERVE_MCP_ALLOWED_ROOTS`)覆盖文件; `--host`/`--port` 标志覆盖两者。
## 开发
```bash
npm install
npm test # 类型检查 + node:test — 核心,http,mcp 往返
npm start # HTTP 服务器
npm run build # tsc -> dist/
```
使用 TypeScript 编写;开发和测试直接通过 Node 的原生类型剥离运行 `.ts`。已发布的包在 **Node ≥ 22.5** 上运行(内置 `node:sqlite`);开发需要 **Node ≥ 22.18**(类型剥离)。
变更记录在 [CHANGELOG.md](CHANGELOG.md) 中;使用 `@dev` 代替 `@latest` 进行边缘安装。
MIT.