关于 fontpool
fontpool 是用于搜索字体目录并将字体安装到项目中的 MCP 服务。它帮助开发者和编码 Agent 根据类别、用途标签及可变轴选择字体,随后下载 WOFF2 文件并生成 CSS,以便自托管。
主要功能
- 01整合搜索三个字体提供方
- 02按类别、标签和可变轴筛选
- 03查看字体详情与字库厂商描述
- 04下载字体文件并生成 @font-face CSS
- 05在项目中自托管字体
适用场景
- 01按类别或设计用途查找字体
- 02选择字体前比较可变轴
- 03将字体文件和 CSS 加入项目
- 04以本地文件替代运行时 CDN 字体请求
MCP 相关
向 MCP 客户端提供字体搜索、属性查询和安装工具。提供工作区根目录时,`install_font` 会将文件和 CSS 写入项目工作区内。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
3收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| tag | string | 否 | 用途标签,例如 'editorial'、'branding'、'legible'、'code'。不带参数调用可列出所有标签。 |
| limit | integer | 否 | 最多返回的字体系列数。默认值为 100。 |
| filter | string | 否 | 按字体系列名称进行不区分大小写的子字符串匹配。 |
| exclude | array | 否 | 排除由这些 provider 提供的字体系列。['google'] 会保留 Google 的 API 不提供的 218 个字体系列。 |
| hasAxis | string | 否 | 仅返回带有此可变轴的字体系列,例如 'wdth'(宽度)、'opsz'(光学尺寸)、'slnt'(倾斜)或 'CASL' 等表现性轴。不带参数调用可列出所有轴。 |
| category | enum | 否 | 限制为一个类别。sans-serif | serif | slab-serif | display | handwriting | monospace | blackletter | icons | other |
| provider | enum | 否 | 限定为一个 provider。省略则涵盖全部。fontshare | fontsource | google |
| variableOnly | boolean | 否 | 仅返回带有可变轴的字体系列。 |
详细介绍
Fontpool
一个用于处理字体的 MCP 服务。它将三个字体提供方的资源整合成一个目录进行搜索,告诉你每个字体系列的实际属性——类别、用途标签、字库厂商自己的描述以及每个可变轴——将真正的 woff2 文件下载到你的项目中,并生成匹配的 @font-face 规则。
字体会自行托管——运行时不会请求 CDN,也不会因第三方更改 URL 而出问题。基于 unifont 构建。
重点不在于方便。让 AI 为页面设置样式时,它几乎每次都会选 Inter 或 Roboto,部分原因是这些名称最常见,部分原因是它知道自己可以加载这些字体。Fontpool 的存在,是为了提供另一种选择:2,168 个字体系列,包含类别、用途标签和字库厂商对每个系列的描述,其中有 218 个不在 Google 的目录中。
提供方
| 提供方 | 提供内容 |
|---|---|
| Fontshare | ITF 自有的字体系列——Satoshi、Switzer、General Sans、Clash Display。另有 65 个其他提供方没有的字体系列 |
| Fontsource | 按子集打包的开源字体系列,带有 unicode-range。另有 127 个其他提供方没有的字体系列 |
| Google Fonts 目录 |
查询顺序就是表格中的顺序,最先找到某个字体系列的提供方会胜出,因此 Satoshi 会解析为 ITF 的正版字体,而不是外观相似的字体。
Google 没有独有字体系列——它的全部内容都包含在排在它前面的 Fontsource 中,因此它从未实际提供过安装文件。保留 Google 只是作为后备方案:unifont 会吞掉提供方自身的目录错误,因此如果 Fontsource 及其依赖的 jsDelivr 无法访问,Google 仍可覆盖约 1,950 个字体系列。
**Bunny 已在 1.3.0 中移除。**它只是 Google Fonts 的镜像,移除它没有任何损失:无论是否包含它,目录中都有 2,168 个字体系列。它曾保留的 26 个已停用的 Google 字体名称——Source Sans Pro、Material Symbols、Big Shoulders 系列——在 Fontsource 中也都有,因此没有字体变得无法访问。它所谓的“隐私优先”也与此无关:fontpool 会下载文件并自行托管,因此运行时不会向任何 CDN 发出请求。
关于 218 这个数字,有一点需要说明:它统计的是 Google API 当前不包含的字体系列,其中约 26 个是 Google 曾经提供、后来停用的,而非从未提供过。Google 从未提供过的字体系列约有 192 个。
**我们有意不支持 Adobe Fonts。**Adobe 的条款要求通过 kit 嵌入从 Adobe 自己的 CDN 加载其字体,因此无法将它们下载到项目中。承诺提供自行托管文件的工具不能如实提供这些字体,因此我们选择完全不支持 Adobe,而不是只提供部分支持。
安装
需要 Node 20 或更高版本。无需 API 密钥或账户。
npx -y fontpool
这也是应填入客户端配置的命令。
Claude Code
claude mcp add fontpool -- npx -y fontpool
Claude Desktop
macOS 上的 ~/Library/Application Support/Claude/claude_desktop_config.json,Windows 上的 %APPDATA%\Claude\claude_desktop_config.json:
{
"mcpServers": {
"fontpool": {
"command": "npx",
"args": ["-y", "fontpool"]
}
}
}
Cursor / Windsurf / Zed 及其他 stdio 客户端
在相应客户端的 MCP 配置中使用相同格式:
{
"mcpServers": {
"fontpool": {
"command": "npx",
"args": ["-y", "fontpool"]
}
}
}
技能
服务器随附 SKILL.md。如果模型从未想到要调用这些工具,它们就毫无用处;而编写 CSS 本身也不会自然地促使模型停下来查找字体——因此,这项技能会在“这里需要一种无衬线字体”和输入字体名称之间加入一个步骤。将它复制到客户端读取技能的任意位置,或直接让你的 Agent 使用它。
工具
list_fonts
搜索目录。不带参数调用时,它会说明目录的结构,而不是列出 2,168 个名称;这也是了解标签词汇的方法:
{}
2168 families:
By category:
sans-serif: 824
display: 500
serif: 379
handwriting: 369
monospace: 72
other: 17
icons: 8
slab-serif: 4
blackletter: 1
Not carried by Google: 218 families (exclude: ["google"] to see only these).
Tags: poster (53), magazine (50), book (37), logo (31), website (29),
banners (28), editorial (23), branding (20), headline (18), app (11),
legible (11), packaging (9), screen (8), code (5), fun (5), …
Variable axes: wght [Weight] (614), ital [Italic] (185), wdth [Width] (97),
opsz [Optical Size] (32), slnt [Slant] (23), ELSH [Element Shape] (15),
CRSV [Cursive] (14), GRAD [Grade] (8), MONO [Monospace] (3), …30 rarer axes
Narrow with category, tag, filter or exclude — listing all 2168 names at
once is rarely the useful answer.
使用筛选条件,可以获得一份值得挑选的候选列表:
{ "category": "serif", "tag": "editorial" }
6 families (category: serif, tag: editorial):
Bespoke Serif — serif · fontshare · Indian Type Foundry · magazine, poster, book, branding, editorial
Crimson Pro — serif · fontshare+fontsource+google · magazine, publishing, editorial
Gambarino — serif · fontshare · Indian Type Foundry · magazine, poster, book, editorial
Karma — serif · fontshare+fontsource+google · Indian Type Foundry · banners, editorial
Rowan — serif · fontshare · Indian Type Foundry · book, text, legible, editorial
Zodiak — serif · fontshare · Indian Type Foundry · editorial, magazine, book
| 参数 | 作用 |
|---|---|
provider | 选择 fontshare、fontsource、google 之一 |
filter | 按字体系列名称进行不区分大小写的子字符串匹配 |
category | sans-serif、serif、slab-serif、display、handwriting、monospace、blackletter、icons、other |
tag | 用途标签 — editorial、branding、legible、code、… |
exclude | 排除这些提供方收录的字体系列。["google"] 会留下 Google API 未收录的 218 个系列 |
variableOnly | 仅返回带有可变轴的字体系列 |
hasAxis | 仅返回带有此轴的字体系列 — wdth、opsz、slnt,或 CASL 这类表现性轴。不区分大小写 |
limit | 返回的字体系列数量上限。默认值为 100,最大值为 500 |
结果会去重。unifont 自己的目录会针对每个收录该字体系列的提供方各返回一次 — 2,168 个系列共 6,124 行,其中约 20k 个 token 大多是重复内容 — 因此这里会合并结果,并改为在每个系列旁列出提供方。
font_properties
查看一个字体系列实际提供什么、是什么样的,再决定是否采用。
匹配字体系列名称时不区分大小写和空格,因此 satoshi 和
jetbrains mono 都能找到对应结果。
{ "family": "Satoshi" }
{
"family": "Satoshi",
"formats": ["woff2", "woff", "ttf"],
"styles": ["normal", "italic"],
"weights": ["300", "400", "500", "700", "900", "300 900"],
"provider": "fontshare",
"availableFrom": ["fontshare"],
"categories": ["sans-serif"],
"tags": ["logo", "magazine", "branding"],
"foundry": "Indian Type Foundry",
"designers": ["Deni Anggara"],
"description": "Satoshi is a modernist sans serif typeface. Its design combines typically grotesk-style letterforms, with some characters that are quite geometrically-designed…",
"descriptionSource": "Indian Type Foundry (marketing copy)",
"variableAxes": {
"wght": { "name": "Weight", "min": 300, "max": 900, "default": 780 }
}
}
可变字体会以范围形式报告字重,例如 "300 900" — 将相同的字符串传给
install_font,即可获取一个可变字体文件,而不是多个静态字体文件。
variableAxes 会列出字体系列包含的所有轴,而不只是字重;这往往正是
选择某款字体而非另一款的原因:
{
"family": "Recursive",
"variableAxes": {
"wght": { "name": "Weight", "min": 300, "max": 1000, "default": 400 },
"slnt": { "name": "Slant", "min": -15, "max": 0, "default": 0 },
"CASL": { "name": "Casual", "min": 0, "max": 1, "default": 0, "step": 0.01 },
"CRSV": { "name": "Cursive", "min": 0, "max": 1, "default": 0.5, "step": 0.1 },
"MONO": { "name": "Monospace", "min": 0, "max": 1, "default": 0, "step": 0.01 }
}
}
小写标签是 OpenType 注册的五种轴;大写标签由字体厂商定义,含义取决于
相应字体的设计。若 step 的默认值为 1(信息量有限),则不会显示。可用
list_fonts { hasAxis: "wdth" } 按任意轴筛选 — 2,168 个系列中有 97 个带有宽度轴,这一点无法通过阅读名称得知。
description 是字体厂商自己的文案,取自提供方目录,并会标明来源。它对设计特点的描述值得信赖,对优点的评价则比较热情;请酌情参考。如果找不到完全匹配项,会提供相近匹配结果,而不只是报告失败:
No provider knows 'Intar'. Did you mean: Inter, NTR, Anta, Inder, Zina?
install_font
下载字体文件并写入 CSS。
{
"family": "Inter",
"targetDir": "./public/fonts",
"cssPath": "./src/fonts.css",
"weights": ["400", "700"],
"styles": ["normal"],
"subsets": ["latin", "latin-ext"],
"urlPrefix": "/fonts"
}
Installed Inter from fontsource.
Files in /Users/you/app/public/fonts:
inter-400-normal-latin-68f645c6.woff2 (23.1 KB)
inter-700-normal-latin-5197a7ae.woff2 (23.8 KB)
inter-400-normal-latin-ext-1bf1f837.woff2 (34.2 KB)
inter-700-normal-latin-ext-c8d55506.woff2 (35.4 KB)
CSS in /Users/you/app/src/fonts.css: 4 rule(s) added, 0 updated, 0 already
present. Import it, then use:
font-family: 'Inter', sans-serif;
Make sure fonts is served at /fonts.
只有 family、targetDir 和 cssPath 是必填项。其余参数默认使用
字重 400 和 700、样式 normal、子集 latin 和 latin-ext,以及
urlPrefix /fonts。
有三点值得注意:
urlPrefix是公开路径,不是磁盘路径。 文件会写入targetDir;CSS 会通过urlPrefix引用这些文件。如果静态资源根目录是public/,那么targetDir: "./public/fonts"应与urlPrefix: "/fonts"配套使用。- 如果样式表没有被任何文件导入,会明确告知。 创建一个没有任何
文件引用的
fonts.css,是唯一一种看起来完全成功的失败情况 — 文件已写入, 规则也正确,但页面仍然以 Helvetica 渲染。如果工作区根目录下没有任何文件引用新样式表,结果会说明这一点,并告知需要添加什么。优先使用项目已经导入的cssPath。 - 重新安装是安全的。 文件名包含内容哈希;当除
src之外的所有描述符都匹配时,规则会被识别为同一个字体样式,因此再次安装会原地更新规则,而不会叠加重复规则。
如果字体系列确实存在,但缺少你指定的字重,结果会明确说明,而不会归咎于名称:
'Satoshi' exists, but not in the combination you asked for (weights 350;
styles normal; subsets latin, latin-ext). It has weights 300, 400, 500, 700,
900, 300 900; styles normal, italic.
元数据的来源
类别、标签、字体厂商和描述读取自两个目录 API — Fontshare 的 100 个字体系列,以及 Fontsource 的 2,103 个;两者合起来覆盖整个目录,因为 Google 本身就是 Fontsource 的子集。 Google 的公开 API 只提供类别信息,因此不会查询它。
可变轴来自第三个端点:
Fontsource 的 /v1/variable。其中的 573 个字体系列按名称关联到目录,没有遗漏。Fontshare 也会发布自己的轴数据,这是它独有的 65 个字体系列能够报告字重范围的唯一途径——它会为每种可变样式列出一条记录,因此同时有可变罗马体和斜体的字体系列会报告两次 wght;这些记录会合并为一个范围;如果各条记录中的 default 不一致,则会将其舍弃,而不是任意选取一个值。
轴的显示名称取自 Google 的 轴注册表,共 56 个标签,采用硬编码而非实时获取:注册表位于一个 2.7MB 的数据载荷中,其余 2.68MB 是 Google 字体列表,而且注册表大约每年才更新一次。复制后新增的标签会原样保留其范围,但不会显示名称。
unifont 已经会获取这两个目录 API 并保留完整记录,但只将它们用于一件事:选择 serif 或 sans-serif 作为 CSS 后备字体。fontpool 不分叉它,而是再次获取这些 API,因此首次使用时会各多发出一个请求——第一次调用工具时总共并行发出三个请求。
Fontshare 的标签是自由文本,因此在公开之前会先进行规范化处理——Poster 和 Posters 会合并为同一个词条,大小写会统一;如果某个标签本身是目录中某款字体的名称,则会将其舍弃,视为数据录入失误。元数据是增强项,不是前提条件:如果三个端点中任意一个无法访问,搜索仍可正常工作,结果也会说明元数据不完整。
允许写入的位置
install_font 会创建目录并向样式表追加内容,因此其操作范围仅限于项目:
- 当客户端声明了工作区根目录时(大多数客户端都会声明),相对路径会以该根目录为基准解析,两条路径都必须位于该目录内——符号链接也包括在内。任何越界路径都会被拒绝并报错。
- 当客户端未声明根目录时,就没有可用的限制范围:相对路径会被直接拒绝;绝对路径则按原样处理,并写入其指向的位置。
开发
npm install
npm test
测试套件会通过真实的 stdio JSON-RPC 将服务作为子进程运行。其中大部分测试会调用实时的提供方 API 并下载真实字体;设置 FONTPOOL_SKIP_NETWORK_TESTS=1 可仅运行离线测试。
./build.sh 会将同一服务打包为 Claude Desktop .mcpb 扩展并放入 ~/Downloads;./build.sh --dev 会将 server.mjs 直接推送到已安装的副本中。
许可证
MIT © Travis Valeri
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。