Skip to main content
Glama

cs2wt-docs-mcp

离线索引 Counter-Strike 2 Workshop Tools 文档,并通过 MCP 提供给 AI 助手使用,同时附带一套 CLI 用于抓取、增量同步与索引维护。

抓取 Valve Developer Community/wiki/<标题> 渲染后的 HTML,转换为 Markdown 后写入单文件 SQLite FTS5 全文索引,之后检索 完全离线、无需再访问网络。

页面以 URL 标题为主键(HTML 通道拿不到 pageid,也不用页面的显示标题)—— URL 标题唯一、稳定、与地址一致。原始 HTML 是 source of truth,索引可随时由它重建。

背景:为什么需要一个 Anubis 求解器

VDC 站点由 Anubis 反爬代理保护,所有客户端 (包括非 Mozilla UA)都会被要求完成一个 SHA-256 工作量证明:

hex(sha256(randomData + str(nonce))) 以 difficulty 个 0 开头

cs2wt.http.AnubisSession 会自动解析挑战、求解、提交并持久化认证 cookie (techaro.lol-anubis-auth,约 7 天有效)。挑战与 User-Agent 和客户端 IP 绑定, 所以整个会话必须保持同一个 UA

Related MCP server: opencode-docs

robots.txt 合规

VDC 的 robots.txt 禁止了 /w/api.php/w/Special: 以及带 title=Special: / action=history 查询的路径。因此本项目不使用 MediaWiki Action API(/w/api.php)。 请求的路径只有两种:渲染页面 /wiki/<标题>,以及用于枚举页面的 /wiki/Special:PrefixIndex/<前缀>(干净路径、无 query,robots 未禁)。

唯一允许的请求形态被固化为运行时不变式:URL 形态由 HTTP 层的守卫 cs2wt.http.assert_allowed_url(url)每次请求前校验,违规立即抛 ValueError(在请求发出前拦截);请求方法由客户端 API 保证(AnubisSession 只暴露 get):

约束

scheme + host

https://developer.valvesoftware.com

path

必须以 /wiki/ 开头

query / fragment

必须为空

path 内容

不得包含 /w/Special: 仅放行 /wiki/Special:PrefixIndex/

method

GET

四条 Disallow 规则均为路径 / 查询匹配,而上述两种路径都不含 query、不以 /w/ 开头, 故不匹配任何一条(有单测覆盖)。

安装

要求 Python ≥ 3.10。抓取 / 解析 / CLI 路径只用标准库;MCP 服务端依赖官方 mcp SDK(mcp>=2,<3,当前唯一的运行时依赖)。

从 GitHub 安装(推荐用 pipx 隔离环境):

pipx install git+https://github.com/Just-Silver/cs2wt-docs-mcp.git
# 或:pip install git+https://github.com/Just-Silver/cs2wt-docs-mcp.git

安装后得到两个入口:cs2wt(CLI)与 cs2wt-mcp(MCP 服务端,stdio)。

MCP 无需手动准备数据:首次运行会自动从 GitHub Release 下载预建索引(见下)。

想自行抓取数据(联网、需通过 Anubis 挑战)或参与开发时,用源码安装:

git clone https://github.com/Just-Silver/cs2wt-docs-mcp.git
cd cs2wt-docs-mcp
pip install -e .
cs2wt fetch     # 全量抓取 /wiki/<标题>,落盘 raw HTML
cs2wt build     # 构建 FTS5 索引

之后检索完全离线。

数据默认放在用户级目录(与当前工作目录无关,CLI 与 MCP 共用):

平台

默认数据目录

Windows

%LOCALAPPDATA%\cs2wt-docs

macOS

~/Library/Application Support/cs2wt-docs

Linux

$XDG_DATA_HOME/cs2wt-docs(默认 ~/.local/share/cs2wt-docs

--data-dir / CS2WT_DATA_DIR 覆盖。

CLI 用法

fetch / sync 会实时抓取源站,供维护者或自建数据;只想检索的话,MCP 会自动下载预建索引,无需抓取。

# 首次全量抓取(默认 prefix: "Counter-Strike 2 Workshop Tools")
cs2wt fetch

# 增量同步:只更新变化的页面(逐页 revid 比对)
cs2wt sync
cs2wt sync --dry-run          # 只报告将发生的变更,不写盘

# 从下载的原始 HTML 构建 FTS5 索引
cs2wt build

# 检索 / 读取 / 列表 / 状态
cs2wt search "material editor" --limit 5
cs2wt get "Counter-Strike 2 Workshop Tools/Level Design/Compiling"
cs2wt list
cs2wt status

全局参数:--data-dir(默认见「安装」的用户级目录)、--db(默认 <data-dir>/docs.sqlite)、--cookie(默认 <data-dir>/cookies.txt)、--ua--delay。 (全局参数需写在子命令之前,例如 cs2wt --cookie cookies.txt fetch。)

MCP 服务端

除 CLI 外,本项目提供一个 MCP 服务端,把离线索引通过 stdio 暴露给 AI 助手。

cs2wt-mcp            # 以 stdio 启动

数据来源是 GitHub Release(滚动 tag data-latest),不访问源站

  • 首次运行:下载预建的 docs.sqlite + manifest.json 到用户级数据目录。

  • 之后每次启动:拉取约 5KB 的 manifest.json 比对 generated_at,仅在远端更新时整包替换; 距上次成功检查不足 24 小时则跳过(CS2WT_CHECK_INTERVAL)。

  • 已有本地索引时,下载失败不影响使用(照常离线检索)。

环境变量(均有默认值):CS2WT_DATA_DIRCS2WT_DBCS2WT_RELEASE_REPOCS2WT_RELEASE_TAGCS2WT_CHECK_INTERVALCS2WT_NO_REFRESH。 设置 CS2WT_NO_REFRESH=1(或 --no-refresh)可完全关闭自动下载 / 检查。

在 OpenCode(V2)中接入

OpenCode V2 在 mcp.servers 下配置 MCP 服务器;本地 stdio 服务器用 "type": "local""command" 数组。把下面这段放进你的项目 opencode.jsonc(或 .opencode/opencode.jsonc), 或全局 ~/.config/opencode/opencode.jsonc——数据目录是用户级的,与工作区无关:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "servers": {
      "cs2wt": {
        "type": "local",
        "command": ["cs2wt-mcp"]
      }
    }
  }
}
  • 索引自动落在用户级数据目录(见「安装」),与 OpenCode 的工作区无关, 所以在任何项目里打开 OpenCode 都开箱即用,无需配置路径。

  • 首次运行会自动从 Release 下载索引(纯 HTTPS,无需 Anubis)。要改到别处、或用 fork 的 产物,再加 "environment": { "CS2WT_DATA_DIR": "...", "CS2WT_RELEASE_REPO": "owner/repo" }

  • cs2wt-mcp 由安装步骤提供。若它不在 PATH 上,把 command 换成 ["python", "-m", "cs2wt.mcp_server"](或写入入口脚本的绝对路径)。

  • 想固定用本地索引、不自动联网检查:加 "CS2WT_NO_REFRESH": "1"environment 的值都是字符串)。

  • 其余可用环境变量:CS2WT_DBCS2WT_RELEASE_TAGCS2WT_CHECK_INTERVAL

也可用 CLI 添加(会写入项目配置,保留其它设置):

opencode mcp add cs2wt -- cs2wt-mcp
opencode mcp list        # 连接正常时显示 connected

暴露的工具:

  • search_docs(query, limit):全文检索,返回 countresults (每项含 titleurlsnippetscore)。

  • get_page(id_or_title, section):读取页面正文,可按章节截取。id_or_title 为纯数字时 按数字 rowid 查询,否则按页面标题查询;返回 foundtitleurlrevidtimestampcontent

  • list_pages():列出全部收录页面(每项含 title)。

标识符由 pageid 改为 title 是本次抓取通道变更的破坏性影响之一。

数据布局

<数据目录>(默认用户级,见「安装」)
  docs.sqlite           # FTS5 索引(title 为主键);MCP 从 Release 下载
  manifest.json         # 每页 title/revid/timestamp/file + generated_at;同上
  last_check.json       # 上次成功检查 Release 的时间(节流用,本地生成)
  raw/<slug>.html       # 原始 HTML(source of truth);仅 CLI 自建时存在
  cookies.txt           # Anubis 认证 cookie;仅 CLI 抓取时使用

架构

模块

职责

anubis.py

解析并求解 Anubis PoW 挑战

http.py

带 cookie jar 与限速的 HTTP 会话,自动过挑战;含合规守卫 assert_allowed_url

wiki.py

HTML 客户端 HtmlClient:PrefixIndex 枚举 + /wiki/<标题> 单页抓取

htmlparse.py

从 HTML 提取元数据 / 链接,并转 Markdown(零依赖)

fetch.py

按前缀全量抓取文档树(raw HTML)

sync.py

增量同步(逐页 revid 比对,新增/更新/删除)

store.py

manifest 与 raw HTML 文件的读写(按 title)

index.py

SQLite FTS5 索引(title 为键,rowid 跨同步稳定)

release.py

从 GitHub Release 下载 / 比对预建索引(MCP 的数据来源)

cli.py

命令行入口

增量同步

cs2wt sync 分两条相互独立的路径:

  • 已有页:对 manifest 中每一页逐页抓取,比对 HTML 中的 oldid(revid)。

    • 返回 404,或页面变成重定向 → 判定删除,从索引与 manifest 移除(raw 文件保留归档);

    • 返回 200 且 revid 变化 → 重抓、覆写 raw、更新索引;

    • 其它失败(超时 / 网络等)→ 单页在客户端层最多重试 FETCH_RETRIES 次 (模块常量 cs2wt.wiki.FETCH_RETRIES,默认 3),仍失败则跳过该页、保留原记录, 计入失败清单(SyncReport.failed),不判为删除。

  • 新页:用 /wiki/Special:PrefixIndex/<前缀> 枚举出全部页面,发现 manifest 之外的 title 并抓取入库。

枚举只用于发现新增;删除只由该页自身的 404 / 重定向决定,因此枚举遗漏不会造成误删。 同步仍是 O(页数) 次普通 GET 请求(条件请求见「后续项」)。

定时更新(GitHub Actions)

.github/workflows/update-docs.yml 负责把数据产物集中持久化到 Release(供下载与分发):

  • 触发schedule 每月 1 日 03:17 UTC 全量抓取一次(fetch + build),并发布到 GitHub Release 滚动 tag data-latest(资产:docs.sqlite + manifest.json)。

  • 手动兜底:另提供 workflow_dispatch。GitHub 公共仓库连续 60 天无活动会自动禁用 定时 workflow(这里的"活动"指仓库活动,而非 workflow 运行);届时可在仓库的 Actions 页面手动触发,或对仓库产生一次提交以重新启用。

  • CI 每次运行的出口 IP 不同,上一轮的 Anubis cookie 不可复用,故每轮都重新求解 PoW, 且不把 cookie 提交进 git(使用 $RUNNER_TEMP 临时路径)。

  • 抓取步骤用 nick-fields/retry 做整脚本重试,只覆盖整轮失败(如 Anubis / 网络整体 不可用);单页失败已由客户端层的单页重试处理,不依赖整脚本重跑。

从旧版本迁移

本次抓取通道由 MediaWiki Action API(pageid 键、raw/*.wiki)切换为 /wiki/<标题> HTML(title 键、raw/*.html)。旧数据无法直接复用(schema 与文件格式 都变了),按以下步骤重建即可:

# 保留 data/manifest.json(fetch 会读取其 title 作为 BFS 种子)
# 删除 data/docs.sqlite        # 旧 schema 不兼容
# 删除 data/raw/*.wiki         # 旧格式,改用 raw/*.html
cs2wt fetch                    # PrefixIndex 全量枚举 + 以现有 manifest 的 title 为种子
cs2wt build                    # 重建 FTS5 索引

不提供 wikitext → HTML 的转换(无意义)。保留 manifest.json 是为了把它已有的 title 并入种子,作为 PrefixIndex 之外的兜底。

若旧数据在仓库内的 ./data:默认数据目录已改为用户级目录(见「安装」),给上面两条 命令加 --data-dir data 即可继续用原位置。

后续项

  • 条件请求(304)优化sync 目前逐页普通 GET,后续可用 If-None-Match / If-Modified-Since 降低带宽;若站点 / Anubis 不支持 304,则回退普通 GET,行为不变。

测试

python -m unittest discover -s tests -v

合规

VDC 内容通常为 CC BY-NC-SA。本项目只请求 robots.txt 允许的 /wiki/<标题>/wiki/Special:PrefixIndex/<前缀> 路径(由 assert_allowed_url 硬约束),保持请求间隔 (默认 --delay 1.0)。请仅作本地个人用途,分发时保留署名与许可。本仓库只包含代码, 不包含抓取到的文档内容。

许可证

MIT

Available Tools

3 tools
get_pageA

读取某个文档页面的正文,可按章节只取一部分。

何时使用:已知页面标题(或数字 rowid),需要阅读其内容;或只想读取某章节以控制返回体积。 何时不使用:只知道模糊关键词时,先用 search_docs 定位。 参数:id_or_title 为纯数字时按数字 rowid 查询,否则按页面标题查询; section 为可选的章节标题(不区分大小写,支持部分匹配)。 返回:JSON,含 found、title、url、revid、timestamp、content; 未找到页面或章节时 found 为 false。索引初始化中或刷新失败时返回 status 字段; 后台刷新进行中时额外带 refreshing 字段。

ParametersJSON Schema
NameRequiredDescriptionDefault
sectionNo
id_or_titleYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the behavioral transparency burden. It discloses not-found behavior (found=false), index initialization/refresh failure status, and background refresh behavior. It does not explicitly mention auth or side-effect freedom, but for a read-only page retrieval tool the disclosed behavior is strong.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-organized into 'when to use', 'when not to use', parameter guidance, and return behavior. Every sentence adds necessary information without redundancy or padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given an output schema exists, the description still adds important contextual details: exact return fields, failure semantics, and background refresh states. It covers the main edge cases an agent needs to correctly interpret results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must fully compensate. It does: id_or_title semantics are clarified (numeric means rowid, otherwise title), and section is documented as optional, case-insensitive, and partially matching. This goes well beyond the raw schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb and resource ('读取某个文档页面的正文') and clarifies the optional section scope. It also differentiates itself from sibling tool search_docs by stating when get_page applies versus when search is needed.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly provides '何时使用' and '何时不使用' conditions: use when the page title or numeric rowid is known, and do not use when only fuzzy keywords are known—use search_docs instead. This gives clear selection guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_pagesA

列出当前索引收录的全部文档页面。

何时使用:需要了解文档覆盖范围、列举全部页面,或确认某主题是否被收录。 何时不使用:已有明确查询词时,用 search_docs 更合适。 返回:JSON,含 count 与 pages(每项含 title)。索引初始化中或刷新失败时返回 status 字段;后台刷新进行中时额外带 refreshing 字段。

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full responsibility. It discloses the return structure (count, pages with titles) and edge-case behavior around status and refreshing fields during index initialization or background refresh, which is valuable behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and well-organized: purpose first, then usage guidance, then return details. Every sentence adds value without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a parameterless listing tool, the description fully covers purpose, usage, return format, and special status conditions. It is complete in context with sibling tools and output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the baseline is 4. No parameter documentation is needed, and the description focuses appropriately on behavior and return values.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the tool lists all document pages in the current index, with a specific verb and resource. It distinguishes itself from siblings by contrasting with search_docs for query-based retrieval and get_page for individual pages.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance: when needing document coverage, enumerating all pages, or checking topic inclusion. Also explicitly says when not to use it and directs to search_docs instead.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_docsA

按关键词检索 Counter-Strike 2 Workshop Tools 官方文档的离线全文索引。

何时使用:需要查找 CS2 Workshop Tools 文档中的术语、命令、概念、实体时; 回答任何涉及该工具集用法的技术问题前,应先用本工具定位来源页面。 何时不使用:已知道确切页面标题(或数字 rowid)时,直接调用 get_page 更高效; 与 CS2 Workshop Tools 文档无关的问题不要使用。 返回:JSON,含 count 与 results(每项含 title、url、snippet、score; score 为 bm25,越小越相关)。索引初始化中或刷新失败时返回 status 字段; 后台刷新进行中时额外带 refreshing 字段。

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden. It describes the return JSON structure (count, results with title, url, snippet, score) and explains the bm25 scoring semantics. It also discloses special status fields for index initialization/refresh failures and background refresh. This is transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (purpose, when to use, when not to use, return format, special conditions). It is concise and front-loaded, with each section adding value without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the main usage scenario, return format, and special status conditions. It does not detail the limit parameter's behavior, but that is in the schema. Given the output schema exists, this is mostly complete, though slightly more parameter context would help.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description should explain parameters. It implies query is a keyword but does not explicitly describe it or the limit parameter. This is insufficient given the lack of schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it searches the offline full-text index of CS2 Workshop Tools official docs by keyword. It also distinguishes from get_page by noting when to use get_page instead, giving a specific verb, resource, and context.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly provides 'when to use' and 'when not to use' sections, stating to use this tool for locating terms/concepts in CS2 docs, and to use get_page when the exact page title is known. It also excludes unrelated queries. This is explicit guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv0.1.0
    • First observedget_page
    • First observedlist_pages
    • First observedsearch_docs

TDQS

A4.7/5.0

Scored across 3 tools

Disambiguation5/5

Each tool serves a clear, distinct purpose: search_docs for keyword lookup, get_page for retrieving a known page/section, and list_pages for enumerating the index. No overlap or ambiguity between them.

Naming Consistency5/5

All tools follow the same snake_case verb_noun pattern: search_docs, get_page, list_pages. The naming is perfectly consistent and predictable.

Tool Count5/5

Three tools is an ideal scope for a documentation retrieval server: search, fetch by identifier, and list coverage. Each tool earns its place with no redundancy.

Completeness5/5

The tool surface fully covers the core workflow for documentation access: discover via search, list available pages, and read page content by title or section. No obvious gaps for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides a local MCP server for searching and retrieving documentation from 22+ open-source projects, enabling AI coding assistants to access up-to-date docs without network dependency.
    11 npm
    2
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    An MCP server that provides AI assistants with searchable access to the full s&box game engine documentation and API reference.
    7
    27 npm
    11
    MIT