Skip to main content
Glama
sudoriaa

Game Asset Finder MCP

by sudoriaa
README.md
# Game Asset Finder MCP

[![CI](https://github.com/sudoriaa/game-asset-finder-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/sudoriaa/game-asset-finder-mcp/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

一个面向游戏开发的“搜索优先”MCP 服务。它同时查询多个公开素材源和可选的本地素材库,把不同站点的结果统一成同一套字段,并做中文关键词扩展、许可证归一化、相关性排序、跨源去重、来源均衡、缓存和游标分页。

项目基于当前 MCP TypeScript SDK v2,使用 stdio 运行,适用于 Codex、ChatGPT 桌面端及其他支持本地 MCP 的客户端。

## 设计与优化

项目采用 provider 注册表和统一结果模型,并重点优化了跨来源搜索层:

- 单个来源失败不会拖垮全局搜索,响应会明确标记各来源状态。
- 关键词权重和中英文游戏术语扩展提升了中文查询的召回率。
- 加入 TTL/LRU 缓存、相同并发请求合并、provider 总时限、有限重试和 `Retry-After`。
- 加入统一许可证过滤、规范 URL 去重、镜像来源合并、稳定排序和来源多样化。
- 用 `limit + nextCursor` 分页,避免一次把大量结果塞进模型上下文。
- 本地索引采用增量扫描、SHA-256、原子写入、允许根目录约束和符号链接跳过。
- 工具面压缩为 4 个,降低代理选错工具的概率。

## 数据源

| id | 内容 | 凭据 | 许可证特点 |
| --- | --- | --- | --- |
| `kenney` | 2D、3D、UI、音频、字体素材包 | 无 | CC0 |
| `opengameart` | 2D、3D、音乐、音效 | 无 | 每个条目不同 |
| `itch` | 免费游戏素材列表 | 无 | 每个作者/页面不同 |
| `openverse` | 图片、纹理、图标参考 | 无 | CC / 公共领域 |
| `ambientcg` | PBR 材质、HDRI、3D | 无 | CC0 |
| `polyhaven` | 纹理、HDRI、3D | 无 | CC0 |
| `freesound` | 音效、录音 | `FREESOUND_API_KEY` | 每个条目不同 |
| `local` | 配置目录中的本地素材 | 本地目录 | 由 sidecar 描述 |

任何远程来源都可能临时超时或限流。返回值中的 `sourceStatuses` 会逐源给出 `ok / partial / disabled / timeout / rate_limited / error`;其他成功来源仍然返回结果。

## 安装

要求 Node.js 20.18.1 或更高版本。

```powershell
git clone https://github.com/sudoriaa/game-asset-finder-mcp.git
cd game-asset-finder-mcp
npm ci
npm run build
npm test
```

也可以执行:

```powershell
.\install.ps1
```

直接启动:

```powershell
node .\dist\index.js
```

stdio 的标准输出是 MCP JSON-RPC 通道,服务日志只写入标准错误。

## 接入 Codex

在 `~/.codex/config.toml` 或可信项目的 `.codex/config.toml` 中加入:

```toml
[mcp_servers.game_assets]
command = "node"
args = ["C:\\path\\to\\game-asset-finder-mcp\\dist\\index.js"]
cwd = "C:\\path\\to\\game-asset-finder-mcp"
startup_timeout_sec = 30
tool_timeout_sec = 90
enabled = true

[mcp_servers.game_assets.env]
GAME_ASSET_LOCAL_ROOTS = "D:\\GameAssets;E:\\SharedAssets"
GAME_ASSET_CACHE_TTL_SECONDS = "600"
```

如果启用 Freesound,建议从本机环境转发密钥,而不是把值写进配置:

```toml
[mcp_servers.game_assets]
env_vars = ["FREESOUND_API_KEY"]
```

也可以用 Codex CLI 添加 stdio server:

```powershell
codex mcp add game-assets -- node "C:\path\to\game-asset-finder-mcp\dist\index.js"
codex mcp list
```

保存配置后重启客户端,再用 `/mcp` 查看连接状态。仓库中的 [mcp-config.example.toml](mcp-config.example.toml) 可直接复制修改。

## MCP 工具

### `search_game_assets`

跨源搜索。主要参数:

```json
{
  "query": "像素风地牢角色",
  "types": ["sprite", "tileset"],
  "sources": ["kenney", "opengameart", "itch"],
  "formats": ["png"],
  "tags": ["retro"],
  "license_policy": "commercial",
  "include_unknown_license": false,
  "limit": 12,
  "refresh": false
}
```

`license_policy` 可取:

- `any`:显示所有结果,并明确标记未知许可证。
- `commercial`:只保留已知可商用的许可证;可用 `include_unknown_license` 放回未知项。
- `cc0`:只保留 CC0 / Public Domain Mark。
- `no-attribution`:只保留已知可商用且无需署名的条目。

下一页把返回的 `nextCursor` 原样放进 `cursor`,其他查询参数保持不变。修改查询条件后继续使用旧游标会返回 `CURSOR_QUERY_MISMATCH`。

`hasMore` 表示当前缓存候选快照里还有下一页,不代表远端站点的全部历史结果都已抓取;需要更深结果时,优先缩小关键词、类型或来源范围并重新搜索。

### `get_game_asset`

读取刚刚搜索到的完整记录:

```json
{ "asset_id": "kenney:roguelike-characters" }
```

返回来源、作者、许可证、预览、文件变体、镜像来源、匹配原因和可直接加入 credits 的署名文本。远程条目先搜索后读取;本地条目也可从索引直接读取。

### `list_asset_sources`

列出所有 provider、启用状态、支持的素材类型、凭据要求、缓存配置和本地索引状态。

### `index_local_assets`

扫描 `GAME_ASSET_LOCAL_ROOTS`:

```json
{ "mode": "incremental", "max_files": 20000 }
```

`incremental` 会复用大小与修改时间未变化的 SHA-256;`rebuild` 会全部重算。索引写到 `GAME_ASSET_DATA_DIR/local-assets.json`,不会修改素材文件。

## 本地素材 sidecar

在 `hero.png` 旁放置 `hero.png.asset.json` 或 `hero.asset.json`:

```json
{
  "title": "Azure Knight",
  "description": "32x32 pixel hero with idle and run frames",
  "type": "sprite",
  "tags": ["player", "knight", "pixel-art"],
  "author": "Studio Name",
  "author_url": "https://example.com",
  "license": "CC-BY-4.0",
  "license_url": "https://creativecommons.org/licenses/by/4.0/",
  "source_url": "https://example.com/azure-knight"
}
```

没有 sidecar 的本地文件仍可搜索,但许可证显示为未知。

## 环境变量

| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `GAME_ASSET_LOCAL_ROOTS` | 空 | 本地素材根目录;Windows 用 `;`,macOS/Linux 用 `:` 分隔 |
| `GAME_ASSET_DATA_DIR` | 项目下 `data` | 本地索引目录 |
| `GAME_ASSET_CACHE_TTL_SECONDS` | `600` | 查询缓存秒数 |
| `GAME_ASSET_CACHE_MAX_ENTRIES` | `200` | 查询缓存最大条数 |
| `GAME_ASSET_PROVIDER_TIMEOUT_MS` | `15000` | 单一 provider 时限 |
| `GAME_ASSET_MAX_RESPONSE_BYTES` | `16777216` | 单次远程响应上限 |
| `GAME_ASSET_MAX_RETRIES` | `2` | 429/临时错误的重试次数 |
| `GAME_ASSET_MAX_LOCAL_FILES` | `20000` | 本地索引默认文件上限 |
| `GAME_ASSET_DISABLED_SOURCES` | 空 | 用逗号分隔的 provider id |
| `FREESOUND_API_KEY` | 空 | 可选 Freesound API key |

## 开发与验证

```powershell
npm run check
npm test
npm run test:unit
npm run test:mcp
```

测试覆盖中文查询扩展、类型识别、许可证策略、URL 规范化、去重、缓存击穿合并、局部 provider 失败、分页与游标、本地增量索引,以及真实 stdio MCP 客户端的 `tools/list` / `tools/call`。

## 结果使用提示

MCP 返回的是素材发现与许可证元数据,不替代来源页的最终条款。未知许可证会明确保留为 `id: null`;发布前可用 `commercial`、`cc0` 或 `no-attribution` 缩小范围,并在 `get_game_asset` 中保存来源与署名信息。

TDQS

A4.2/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a distinct purpose: search, get details, list sources, and index. There is no overlap in functionality, making it easy for an agent to select the right tool.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case (get_game_asset, list_asset_sources, search_game_assets, index_local_assets). This predictable structure aids agent understanding.

Tool Count5/5

Four tools cover the essential operations for a game asset finder: search, get detail, list sources, and index local assets. The count is well-scoped for the server's stated purpose.

Completeness5/5

The tools provide a complete workflow: index local assets, search across sources, retrieve detailed records, and list source info. No obvious gaps exist for the domain of finding and retrieving game assets.

Maintenance

ActivitySlowing
ResponsivenessNo issues