# MCP 接入、AI 可发现性与人与 AI 访问计数

> 本文记录 FFRS Wiki 从「只给人看的静态站」升级为「可被 AI agent 直接调用的服务」的完整做法。
> 起点是 `mcp-website-promotion.md` 里的判断：SEO 拼「被搜到」，GEO 拼「被引用」，MCP 拼「被调用」。
> 本 wiki 属于典型的查询类/工具类站点，是最适合 MCP 化的一类：只读、结构化、无账号体系。

---

## 1. 已上线清单（2026-11-08）

| 层 | 内容 | 地址 |
| --- | --- | --- |
| AI 说明书 | `llms.txt`：站点是什么、意图 → 工具映射、数据集、引用方式 | `/llms.txt` |
| MCP 端点 | Streamable HTTP（JSON-RPC 2.0），只读、无需鉴权 | `POST /mcp` |
| MCP 服务卡 | 工具清单、能力、端点、语言 | `/.well-known/mcp.json` |
| A2A 名片 | agent card，8 个 skill 及示例问法 | `/.well-known/agent.json` |
| Registry 元数据 | 官方 Registry 发布用的 `server.json` | `/server.json` |
| 域名验证 | Ed25519 公钥（HTTP 域名认证） | `/.well-known/mcp-registry-auth` |
| 访问统计 | 人类 / AI 分别计数，14 天趋势、Top 客户端与工具 | `/stats.html`、`/api/stats` |
| 搜索引擎主动推送 | IndexNow，22 条 URL，每日按 sitemap 变化自动重提交 | `/<key>.txt`、`tools/indexnow_submit.py` |
| 传输加密 | Let's Encrypt 证书，80 / 443 双栈 | `https://ffrs.redqd.com/` |

官方 Registry 条目：**`com.redqd.ffrs/ffrs-wiki@1.0.0`**（status `active`）。
查询：`https://registry.modelcontextprotocol.io/v0/servers?search=com.redqd.ffrs`。
按 name 直查需带版本：`/v0/servers/com.redqd.ffrs%2Fffrs-wiki/versions/1.0.0`（不带版本的直查路径返回 404）。

---

## 2. 架构

```
浏览器 / AI 爬虫 / AI agent
        │  HTTPS
        ▼
   Nginx（80 + 443）
   ├── /            → 静态 SPA（index.html / about.html / data / media）
   ├── /llms.txt, /.well-known/*, /server.json → 静态文件
   ├── ^~ /mcp     → 反代 127.0.0.1:8765   ─┐
   ├── ^~ /api/    → 反代 127.0.0.1:8765   ─┤
   └── = /health   → 反代 127.0.0.1:8765   ─┘
                                             ▼
                     ffrs-mcp.service（Python 3 标准库）
                     ├── 读取 data/wiki-data.js（3,651 条 + 5,373 条官方标签）
                     ├── 处理 MCP JSON-RPC，输出 9 语言结构化 JSON
                     └── 解析 Nginx access log + MCP 事件日志 → /api/stats
```

服务端程序 `tools/ffrs_mcp_server.py` **只用 Python 标准库**（`http.server` + `ThreadingHTTPServer`），
没有 FastAPI / SDK 依赖，单文件即可运行，升级只需替换一个文件并重启服务。

`^~` 前缀是必须的：站点配置里有一条宽泛的正则 `location`，不加 `^~` 会吞掉 `/mcp` 与 `/api/` 并关闭其访问日志。

---

## 3. MCP 协议实现

- 协议版本 `2025-06-18`（`initialize` 会回显客户端声明的受支持版本）。
- 支持 `initialize`、`notifications/*`（202 空响应）、`ping`、`tools/list`、`tools/call`、
  `resources/list`、`resources/read`、`resources/templates/list`、`prompts/list`、`prompts/get`、
  `logging/setLevel`；未知方法返回 `-32601`。
- 支持 JSON-RPC 批量请求、通知、`Mcp-Session-Id`、`MCP-Protocol-Version` 头、CORS 预检。
- 客户端 `Accept` 只含 `text/event-stream` 时返回 SSE（`event: message` + `data:`）；其余返回 `application/json`。
- `GET /mcp` 返回 405（本服务不提供独立 SSE 长连接），`DELETE /mcp` 结束会话。
- 工具结果同时给 `content[].text`（便于模型阅读）与 `structuredContent`（便于程序解析），
  每条结果附稳定的 `url` 供引用。

### 8 个工具（描述按「用户会派什么任务」写，而不是按数据表写）

| 工具 | 触发意图 | 主要参数 |
| --- | --- | --- |
| `search_ffrs` | 「轮回之翼是什么」「What is Crimson Saber?」「イフリートのステータス」 | `query`, `lang`, `kinds`, `limit` |
| `get_entity` | 已知 ID 后取完整字段 | `kind`, `id`, `lang`, `all_languages` |
| `list_dataset` | 浏览 / 导出某一类清单 | `kind`, `limit`, `offset`, `query`, `lang` |
| `compare_equipment` | 「10300 和 10309 有什么区别」 | `ids`, `lang` |
| `find_sources` | 「这件装备哪里获得」「幻兵怎么拿」 | `id`, `kind`, `lang` |
| `find_drops` | 「マンドラゴラ掉什么、能偷什么」 | `enemy_id` 或 `query`, `lang` |
| `lookup_label` | 内部键名 / UI 词条的 9 语言官方译名 | `key`, `lang` |
| `wiki_stats` | 数据规模、语言支持、人与 AI 访问数 | 无 |

`kind` 取值：`equipment / items / skills / effects / characters / visions / summons / enemies /
quests / shops / maps / trophies / glossary / achievementConds`（14 类）。

**本地化做法**：官方 `name` / `desc` 直接取 `Game.locres` 的 9 语言文本；其余导出字符串经
`data/wiki-data.js` 的 `labels` 表（5,373 条，本身带 9 语言）递归翻译。
**输出的 JSON 键名保持原样不翻译**，只有值随 `lang` 变化，保证不同语言下结构一致、程序可解析。

资源 URI：`ffrs://overview`、`ffrs://languages`、`ffrs://datasets`、`ffrs://stats`、`ffrs://llms`。

---

## 4. 官方 Registry 上架

1. 生成 Ed25519 密钥对；公钥写成 `v=MCPv1; k=ed25519; p=<base64>` 放到 `/.well-known/mcp-registry-auth`。
   私钥保存在服务器受保护目录（权限 600），**不随站点发布、不写入任何文档**。
2. `mcp-publisher login http --domain ffrs.redqd.com --private-key <hex>` —— 用 HTTP 域名验证，
   不需要 GitHub 账号。命名空间必须是域名的反向 DNS：`com.redqd.ffrs/ffrs-wiki`。
3. `mcp-publisher validate` → `mcp-publisher publish`。

**踩过的坑**：Registry 的 `description` 上限是 **100 字符**，超长会返回 422；
官方 CLI 会提示迁移到最新 schema，但对 remote server 而言 `2025-10-17` 与 `2025-12-11`
的差异只是新增 URL 模板变量，无需改动。

发布后条目被下游聚合器、目录站与索引器自动编目，这正是「一次上架、全网广播」的那一步。
验证收录：

```bash
curl "https://registry.modelcontextprotocol.io/v0/servers?search=com.redqd.ffrs"
curl "https://registry.modelcontextprotocol.io/v0/servers/com.redqd.ffrs%2Fffrs-wiki/versions/1.0.0"
```

---

## 5. 人与 AI 分别计数

### 数据源

| 来源 | 作用 | 为什么需要 |
| --- | --- | --- |
| Nginx combined access log | 所有 HTTP 请求（含不执行 JS 的爬虫） | 覆盖 AI 爬虫、MCP 握手、静态页浏览 |
| `mcp-events.jsonl` | 每次 `/mcp` 请求的 method / tool / UA / IP | 日志看不到具体调用了哪个工具 |

计数器按**自然日分桶并覆盖写入**（不是累加），因此重复刷新统计页、服务重启、日志轮转都不会重复计数；
历史日期保存在 `counters.json` 中，日志轮转后累计数字不归零。

### 分类口径

| 类别 | 判定 |
| --- | --- |
| `human` | 浏览器 UA，且不含 bot / headless / CLI 特征 |
| `ai` | AI 爬虫（GPTBot、OAI-SearchBot、ClaudeBot、Perplexity、Google-Extended、Bytespider、混元、CCBot 等）**+ 所有 `/mcp` 请求** |
| `search_bot` | Googlebot / Bingbot / Baiduspider / Sogou / 360Spider 等传统搜索引擎 |
| `monitor` | Headless、curl / wget、python-requests、uptime 监控、通用 bot |
| `other` | 无法识别的 UA |

- 单位：一次**非静态** HTTP 请求 = 一次访问；图片 / JS / CSS / favicon / `/health` / `/api/stats` 自身不计入，避免统计页每 60 秒自动刷新把自己算进去。
- 独立访客：当日按 `IP + UA` 去重，只统计今日，不写跨日用户档案。
- **无 Cookie、无 localStorage 跟踪、无跨站追踪、无第三方分析脚本**；落地页的文案据此改为「匿名、人类与 AI 分别计数」。

### 展示

- `/stats.html`：9 语言、浅/深主题；人类 / AI / MCP 调用 / 全部请求四张卡，14 天双色柱状趋势，
  分类占比、AI 客户端 Top、MCP 工具 Top、热门页面、最近访问、口径说明，每 60 秒刷新。
- 主站首页 hero 下方实时计数条 + 侧栏「AI 与统计」分组（访问统计 / AI 接入）。
- 本地 `file://` 预览或接口不可用时计数条静默隐藏，不产生错误文本。

---

## 6. 部署与维护

| 项 | 位置 / 命令 |
| --- | --- |
| 服务程序 | `/opt/ffrs-mcp/ffrs_mcp_server.py`（站点目录之外，避免源码被下载） |
| 状态目录 | `/opt/ffrs-mcp/state/`（`counters.json` + `mcp-events.jsonl`，属主 `www`） |
| 常驻 | `systemctl enable --now ffrs-mcp.service`，崩溃自动重启 |
| 反代 | 站点 `extension/*.conf` 中的 `mcp.conf`（宝塔面板不会覆盖该目录） |
| 证书 | Let's Encrypt，`acme.sh --install-cert` 的 reloadcmd 已设为 `nginx -s reload`，到期自动续期 |
| 数据更新 | 替换 `data/wiki-data.js` 后 `systemctl restart ffrs-mcp.service`（程序按 mtime 自动重载索引） |
| 日志 | 服务输出 `/opt/ffrs-mcp/server.log`；站点访问日志即计数源 |

常用检查：

```bash
curl -s https://ffrs.redqd.com/health
curl -s https://ffrs.redqd.com/api/stats | python3 -m json.tool | head
curl -s -X POST https://ffrs.redqd.com/mcp \
  -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
python3 tools/verify_mcp.py                              # 本地自启服务，38 项断言
python3 tools/verify_mcp.py --base https://ffrs.redqd.com # 线上回归
```

---

## 7. 隐私边界

- 公开文件（含本文）不含任何私钥、登录凭据、开发者本机路径或账号信息；
  Registry 验证私钥仅保存在服务器受保护目录。
- 访问日志是服务器常规运维日志，统计只读取其中的时间、路径、状态码、UA、IP；
  IP 仅用于当日去重哈希，不输出、不落库、不对外展示。
- MCP 端点只读，不写数据、不接受上传、不执行任意代码，请求体上限 2 MB。

---

## 8. 效果观测建议

上架后的头几周重点看 `/api/stats` 里的三个变化：

1. `top_ai_clients` 是否出现新的 AI 爬虫 / agent 厂商；
2. `ai` 与 `mcp_tool_calls` 是否从 0 开始增长（协议握手、工具清单收录通常先于真实调用）；
3. `top_tools` 里哪个工具被调用最多 —— 它反映了 agent 实际把本站当成什么用，
   后续可以按意图补工具描述或新增工具，而不必改数据管线。
4. `totals.search_bot` 是否从 0 开始增长 —— 这是 IndexNow / GSC 是否真正把 URL 推进索引的直接信号；
   若 `top_ai_clients` 出现新的搜索型爬虫（Bingbot、GPTBot、ClaudeBot），说明搜索层与 AI 层已打通。

---

## 9. Registry 之外的分发渠道

官方 Registry 解决的是「被 AI 调用」，但**搜索层收录是另一条链路**，两者不能互相替代。
按 `mcp-website-promotion.md` 追加的渠道清单逐项核对（`docs/推广素材.md` 第 12 节有完整对照表）：

| 渠道 | 状态 | 备注 |
| --- | --- | --- |
| 官方 MCP Registry | 已完成 | HTTP 域名验证，条目 `active` |
| `llms.txt` + `robots.txt` + `sitemap.xml` | 已完成 | 25 条 URL，显式欢迎 8 类 AI 爬虫 |
| A2A agent card | 已完成 | `/.well-known/agent.json` |
| **IndexNow** | **本次补齐** | 无需账号；根目录 `<key>.txt` 验证 + `tools/indexnow_submit.py` 推送 22 条 URL，cron 每日 `--only-changed` |
| GSC / 百度站长平台 | 需账号 | 唯一无法全自动的环节；GSC 验证文件可代传，百度需先备案 |
| llms.txt 目录站 | 需人工表单 | 文件已就位，随时可提交 |
| Smithery / Glama / PulseMCP 等 | 需公开 GitHub 仓库 | 先开源再叠加；官方 Registry 已覆盖自动编目 |

### IndexNow 实现要点

- 纯 Python 标准库，无第三方依赖；读 `sitemap.xml` 的 `<loc>`，过滤 `.well-known/` / `server.json` 等机器文件。
- 状态指纹 = URL 列表 + **sitemap 原始字节**；`gen_about.py` 的 `<lastmod>` 改为按当天日期生成，
  因此重建 sitemap 会产生新指纹，cron 仅在变化时推送，避免每天重复提交。
- 首次提交返回 `202`，复跑 `200`，未变化时 `--only-changed` 输出 `SKIP`。
- key 是公开的所有权验证串，不含任何凭据；状态与日志在服务器 `/opt/ffrs-mcp/state/`，不随站点发布。

> 本 wiki 为**非官方粉丝资料站**，与 SQUARE ENIX 无关联。MCP 端点与 Registry 条目均为社区服务，
> 游戏文本与图像版权归原权利方所有。
