关于 LicenseGuard
LicenseGuard 是面向开发者的 MCP 服务,用于判断依赖许可证是否会因软件分发方式产生义务。它按 SaaS、内部使用和软件分发等情境评估许可证条款,而非仅列出许可证名称。
主要功能
- 01按分发方式评估许可证
- 02检查单个依赖或清单
- 03跨分发情境解释许可证
- 04支持六种包生态
- 05读取锁文件与 JSON SBOM
适用场景
- 01添加依赖前检查许可证
- 02审计项目锁文件中的义务
- 03比较 SaaS 与分发场景
MCP 相关
向 MCP 客户端提供检查依赖、扫描清单和解释许可证要求的工具,可通过托管的 Streamable HTTP 或本地 stdio 服务使用。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://license-guard.rcc-aoki.workers.dev/mcpclaude mcp add --transport http rccaoki-wq-license-guard https://license-guard.rcc-aoki.workers.dev/mcp{"mcpServers":{"rccaoki-wq-license-guard":{"type":"http","url":"https://license-guard.rcc-aoki.workers.dev/mcp"}}}在 Cursor 中打开{"servers":{"rccaoki-wq-license-guard":{"type":"http","url":"https://license-guard.rcc-aoki.workers.dev/mcp"}}}在 VS Code 中打开{"mcpServers":{"rccaoki-wq-license-guard":{"type":"http","url":"https://license-guard.rcc-aoki.workers.dev/mcp"}}}{"mcpServers":{"rccaoki-wq-license-guard":{"serverUrl":"https://license-guard.rcc-aoki.workers.dev/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
ghcr.io/rccaoki-wq/license-guard:1.1.0stdio工具
3| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 是 | 清单中所写的软件包名称,例如 "express"、"requests"、"github.com/gin-gonic/gin" 或 "serde"。 |
| scope | enum | 否 | 依赖项所在的位置。对于不会包含在交付制品中的内容,请使用 "dev"、"build" 或 "test"——这些都不产生分发义务。默认为 "runtime"。runtime | dev | build | test | optional |
| version | string | 否 | 如果已知,请指定确切版本。省略时将使用最新发布版本,该版本可能与已安装版本不同。 |
| ecosystem | enum | 是 | 依赖项所属的软件包注册表。npm | pypi | go | cargo | rubygems | nuget |
| distribution_model | enum | 是 | 包含此依赖项的软件如何交付给用户。这决定判定结果:"saas" = 用户通过网络访问;"distributed-binary" = 作为应用或二进制文件交付;"on-prem-delivery" = 安装在客户环境中;"internal-only" = 不离开你的组织;"library-published" = 发布供他人依赖的库。saas | distributed-binary | on-prem-delivery | internal-only | library-published |
详细介绍
LicenseGuard
English | 日本語
另见 mcpservers.org、Smithery、TensorBlock MCP Index 和 Docker MCP catalog。
这个依赖项的许可证会对你交付软件的方式产生什么义务?
通用许可证扫描器回答的是另一个问题——“这是什么许可证?”——然后对所有内容发出警告。LicenseGuard 会根据你的分发模式评估许可证,因此同一许可证也会因软件交付给用户的方式不同而得出不同结论。
在线体验:https://licenseguard.tenchorooms.com
下方的 workers.dev 源站与此部署相同,作为 MCP 目录所指向的稳定端点保留。
为什么分发模式会决定结果?
| 交付方式 | AGPL-3.0 依赖项 |
|---|---|
| SaaS(可通过网络访问) | 禁止 — 第 13 条网络条款 |
| 仅供内部使用 | 允许 |
| 分发二进制文件 / 本地部署 | 禁止 — 继承 GPL 分发条款 |
| devDependency(不会进入制品) | 允许 |
最后一行正是关键所在。采用 AGPL 许可证的构建时检查工具永远不会随软件一起交付,因此不会触发任何义务——但仍对此发出警告的工具会让人们学会忽略它们发出的所有警告。
同样的区分贯穿其他许可证体系,而且这些区别不可互换:
- GPL 的义务与分发相关。将 GPL 代码作为网络服务运行不属于分发。
- AGPL 增加了第 13 条,该条款与网络交互相关——这与 GPL 的分发条款是不同的触发条件。在分发情形中引用第 13 条是错误的,LicenseGuard 不会这样做。
- MPL / EPL / CDDL 适用于文件范围,且与链接方式无关。MPL-2.0 第 3.3 条明确允许在更大作品中采用你自己的条款进行分发。将 LGPL 的重新链接逻辑套用到这些许可证上会导致误报。
- LGPL 才是真正取决于链接方式的许可证:静态链接会产生重新链接义务,动态链接则不会。
结论会作为事实陈述,并注明所依据的条款。LicenseGuard 不会告诉你该怎么做。
常见问题
如果我只托管软件,从不分发,GPL 是否适用? 不适用。GPL-3.0 的义务在分发时触发。托管式 SaaS 不属于分发,因此目前不会产生义务——但之后以本地部署、二进制文件或已发布库的形式发布同一软件,就会触发披露整个作品源代码的义务。
如果我只将软件作为 SaaS 托管,AGPL 是否适用? 适用。AGPL-3.0 §13 要求向通过网络与修改版本交互的用户提供整个作品的对应源代码。这一条款使 AGPL 在托管服务方面与 GPL 表现不同,也是两者之间唯一实际的区别。
构建时依赖和开发依赖会产生许可义务吗? 不会。开发依赖不属于你发布的制品,因此不会产生由分发触发的义务。会将代码生成到你的输出中的工具(例如代码生成器)属于另一种情况,值得单独核查。
MIT 适合商业用途吗? 适合,适用于所有分发模式。MIT 只要求注明出处,没有其他要求。Apache-2.0 也能达到同样的结果,同时增加专利授权和 NOTICE 要求——这些是义务,但不会因发布方式不同而变化。
静态链接会改变结论吗?
只有 LGPL 会,且仅限编译型语言。LGPL 的设计是:你可以在专有作品中使用该库,前提是用户能够替换它;动态链接可以做到这一点,而静态链接不行。Go 和 Rust 默认采用静态链接,而 Cargo.lock 或 go.sum 中都没有说明这一点。
为什么同一个许可证对不同项目会得出不同结论? 因为 Copyleft 义务与事件相关——分发,或允许用户通过网络交互——而不是与代码是否存在相关。这些事件是否发生,取决于你的业务,而非你的代码仓库。因此,只读取锁文件的扫描器无法判断这一点。
在编码 Agent 中使用
当你添加依赖时需要它,而不是在网上搜索时。因此,主要使用方式是 MCP 工具,而非搜索结果。
托管版——无需安装:
claude mcp add licenseguard --transport http https://license-guard.rcc-aoki.workers.dev/mcp
本地版(stdio)——你的清单不会离开你的机器。只有软件包名称和版本会发送到公共注册表以查询许可证:
claude mcp add licenseguard -- docker run -i --rm ghcr.io/rccaoki-wq/license-guard:1.1.0
该镜像会在每次发布时发布,并在官方 MCP 注册表中声明为 OCI 软件包,因此能够读取该注册表的客户端无需进行上述任何操作即可安装它。若要自行构建:docker build -t licenseguard . && docker run -i --rm licenseguard。
两种方式都运行同一套策略引擎。它们不会得出不同结论——端到端测试套件(npm run e2e:stdio)会确保两者保持一致。
无状态 Streamable HTTP,无需身份验证,也没有会话状态。
| 工具 | 调用时机 |
|---|---|
check_dependency_license | 添加单个依赖之前 |
check_manifest_licenses | 审核整个清单或锁文件 |
explain_license | 查看某个许可证在每种分发模式下的要求 |
在其他任何场景中使用
JSON API 会返回相同的判定结果:
curl "https://license-guard.rcc-aoki.workers.dev/api/pkg/pypi/pyload-ng?model=saas"
# => {"license":"AGPL-3.0-only","verdict":"blocked", ...}
扫描整个锁文件:
curl -X POST https://license-guard.rcc-aoki.workers.dev/api/scan \
-H 'content-type: application/json' \
-d "$(jq -Rs '{content: ., distributionModel: "saas"}' package-lock.json)"
面向 Agent 的索引位于 /llms.txt。
支持的清单
生态系统:npm · PyPI · Go modules · crates.io · RubyGems · NuGet
| 格式 | 传递依赖 | 注册表查询 |
|---|---|---|
package-lock.json | 是 | 无 — 许可证已嵌入 |
pnpm-lock.yaml、yarn.lock | 是 | 是(通过共享缓存摊销) |
go.sum | 是 | 是 |
Cargo.lock、poetry.lock、uv.lock | 是 | 是 |
Gemfile.lock | 是 | 是 |
packages.lock.json | 是 | 是 |
package.json、requirements.txt、pyproject.toml、go.mod、Cargo.toml | 仅直接依赖 | 是 |
.csproj、Directory.Packages.props、packages.config | 仅直接依赖 | 是 |
| CycloneDX(JSON)、SPDX(JSON) | 是 | 仅在文档未包含许可证时查询 |
有问题的许可证通常来自某个依赖的依赖,而不是你有意添加的内容,因此锁文件才是关键。package-lock.json v2/v3 为每个条目都嵌入了许可证,这意味着可以对所有传递依赖进行完整审计,且无需进行任何网络查询,同时还能使用实际将要安装的确切版本。
不完整的扫描绝不会报告为干净。 无法解析的依赖会标记为 not-checked 或 review,并计入摘要。它们绝不会变成 allowed。
单次扫描最多执行 300 次注册表查询,从而限制单个请求的成本。缓存中的包不会占用此额度,因此只有从未查询过的包才会触及上限——首次扫描约有 1000 个 crate 的 Cargo.lock 时,通常会有几十个条目标记为 not-checked;再次扫描即可解析它们(实测:servo 的 1043 个 crate 在第二次扫描后,未解析的已发布 crate 数量降为零)。结果会明确说明这一点,而不会悄悄显示一份更短的列表。
Git 依赖项和正在扫描的工作区成员会在所有能够识别它们的格式中标记为 not-published,包括 Cargo.lock 的 source 字段、yarn 的 workspace: 和 git+ 协议、pnpm 的 tarball URL,以及 package-lock.json 的 resolved。公共注册表没有它们的许可证数据,因此不会查询它们。这与触及查询上限的情况不同:重新扫描也无法解析它们,结果会明确说明这一点。
这不仅关系到速度。工作区成员可能使用一个恰好也存在于公共注册表中的包名,例如 utils、core 或其他通用名称;否则系统会针对那个无关的公共包进行解析,并将其报告为 allowed,因为工作区版本(0.0.0-use.local)无法匹配任何内容,查询便会回退到最新版本。标记来源才能防止私有包继承陌生包的许可证。
私有注册表是有意保留的例外。指向 npmjs 以外地址的 resolved URL,同样可能是透明的 Artifactory 或 Nexus 代理,用于提供真实的公共包,而锁文件无法区分两者。因此系统仍会查询这些包;如果查询失败,仍会将其报告为 unresolved。
SBOM
CycloneDX 和 SPDX 会以 JSON 格式读取,包括 GitHub 的 {"sbom": …} 封装,因此可以直接粘贴 gh api repos/OWNER/REPO/dependency-graph/sbom 的响应,无需修改。文档中记录的许可证会原样使用;未记录许可证的组件则会进行查询。NOASSERTION、NONE 和 LicenseRef-* 不会被视为声明,因为它们表示文档并不知道许可证是什么。
对于真实 SBOM 中的两种情况,我们会如实报告,而不是加以掩饰,因为这两种情况都会改变结果的含义:
大多数组件都没有可用的许可证。 在五份已发布的 GitHub SBOM 中,expressjs/express 的 44 个组件里有 8 个由文档提供许可证,tokio-rs/tokio 的 51 个组件里有 1 个。其余组件今天会通过注册表查询。结果会说明这一比例,而不会声称许可证来自文档。
版本范围不是版本。 GitHub 的依赖关系图导出会将清单中的范围(^2.0.0、>= 0.2.42,< 0.3.0)同时写入 purl 和版本字段:express 的 44 个组件中有 36 个,tokio 的 51 个组件中有 50 个。不存在名称为该字符串的发行版,因此,直接传入该值的扫描器会根据最新发行版进行解析,却显示一个并不存在于你的制品中的版本。范围不会被接受为版本,结果会说明受影响的组件数量。锁文件没有这个问题,这也是实际使用中优先选择锁文件的原因。
被排除的组件会逐一计数并列出,而不是直接丢弃;排除原因比数量更重要。GitHub Actions 并非缺口。 它们在 CI 中运行,绝不会进入制品,因此不会产生分发或网络义务——所以它们会被排除,结果也会明确说明这一点,而不会将其归为“不支持”。这体现了软件本身的事实与工具的局限之间的区别,并非可以忽略的误差:这五份 SBOM 的 194 个组件中,有 52 个属于 githubactions,是仅次于 cargo 的第二大类型。扫描确实无法读取的包类型(maven、deb 等)会单独报告。
如果文档中没有任何内容可供检查,系统会说明原因并拒绝处理,而不会返回一份空白的正常报告——这两种原因并不相同。gorilla/mux 的 SBOM 只有 GitHub Actions,因此答案是:文档中没有任何你会随软件一起分发的内容;换用其他扫描器也不会改变这一点。
状态
阶段 0——验证付费意愿。 MCP 服务和免费 Web 工具均已上线。由于实际使用场景是在 Agent 工作流中,而不是搜索结果页面,因此衡量的信号是 MCP 安装量和工具重复调用量,而非点击率。只有出现这一信号后,才会启动 GitHub App(阶段 1)。
开发
npm install
npm test # unit tests
npm run typecheck
npm run coverage
npm run smoke # live registry connectivity
npm run e2e # end-to-end against production
# ui real browser (Playwright)
# a11y accessibility
# mcp official MCP SDK client
# load consistency under concurrency
# adversarial hostile input and boundaries
# correctness against known-good verdicts
# operational cross-path agreement, HTTP, caching
# stdio local and hosted paths must agree
npm run dev # http://localhost:8787
npm run signals # Phase 0 report: real usage only, test traffic excluded
npm run signals 决定接下来采取什么行动,因此刻意采取保守策略:标记为合成流量的请求(每个 E2E 测试套件都会设置 x-licenseguard-synthetic: 1)、来自注册表爬虫和评分机器人的流量,以及归因功能推出前写入的行,都会被排除并单独报告,而不会被悄悄丢弃。凡是无法归因的数据,都不会被计作需求。
通过单元测试还不够。若干真实缺陷只有在接入实时注册表数据后才会出现,因此,发布前必须通过针对真实上游服务运行的 smoke 和 e2e 测试。
部署:
npm run db:migrate
npm run deploy
基于 Cloudflare Workers + Hono + D1 构建。
文档
- 设计规范:docs/specs/2026-08-19-license-guard-design.md(日文)
- 阶段 0 实施计划:docs/superpowers/plans/2026-08-19-phase0-free-web-tool.md(日文)
- 注册表发布说明:docs/PUBLISHING.md(日文)
依赖项
鉴于本项目的主题,所有依赖项都特意选用了 MIT 或 Apache-2.0 许可。本项目不包含任何会要求在 SaaS 部署时公开源代码的依赖项。
| 作用 | 软件包 | 许可 |
|---|---|---|
| SPDX 表达式解析 | spdx-expression-parse | MIT |
| Web 框架 | hono | MIT |
| Go 许可数据 | deps.dev API(备用:ClearlyDefined) | Apache-2.0 |
隐私
托管服务不会存储清单内容或 IP 地址。它记录的是使用情况概况:调用了哪个工具、使用了哪个生态系统和分发模式、返回了什么判定结果,以及一个不透明的会话标识符,用于统计重复使用次数。上述记录中不会出现任何软件包名称。确切字段请参见 src/mcp/telemetry.ts。
软件包名称确实会存储在一处,具体位置值得说明:成功的查询会以 (ecosystem, package, version) → SPDX id 的形式缓存,因此后续调用无需再次访问注册表。该表的写入方式带来以下三点,每一点都有测试覆盖:
- 没有用于记录查询者的列。 行中不含会话、请求或地址,因此无法通过缓存追溯到用户 — 参见
migrations/0001_init.sql。 - 无法解析的名称绝不会写入(
src/resolver/index.ts会在写入缓存前返回)。未发布到公共注册表的软件包(例如内部软件包)正是无法解析的情况。 - 缓存并非私有:缓存内容会用于生成
/sitemap.xml。其中的所有内容都已以相应名称发布在 npm、PyPI、Go 或 crates.io 上。
会话标识符以规范规定的 Mcp-Session-Id 标头形式发放。它是一个随机值,离开此数据库便没有任何含义;它不是必需项,也永不过期 — 忽略它的客户端仍可正常工作。
如果组织仍不愿分享这么多信息,可以运行本地 stdio 服务。它只会发送软件包名称和版本,且仅发送给已公开发布这些信息的公共注册表。
有一点值得明确说明,以免有人之后才发现:托管站点的 HTML 页面包含 Cloudflare Web Analytics,因为该站点所在的区域现在已启用此功能。它不使用 Cookie,仅收集汇总数据,但这是一个第三方脚本,而且域名迁移之前并没有这个脚本。该脚本仅注入 HTML 页面 — /mcp、/api/*、/llms.txt 和 /sitemap.xml 均不受影响,因此 Agent 和 API 调用方不会加载它。
免责声明
LicenseGuard 提供基于已发布的许可文本和声明的依赖项元数据得出的信息。这些信息不构成法律建议,使用本服务也不会建立律师与客户之间的关系。判定结果依据软件包声明的许可信息,不代表能够识别所有义务或违规行为。
许可
Apache-2.0
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。