pwa-sticker-mcp
by gobly2333
README.md
# PWA Sticker MCP · 共享表情包
让人和 AI 共用同一个 PWA 表情包库:人在抽屉里点一下发送,模型通过 MCP 调同一张图发送。
The human-facing PWA drawer and the AI-facing MCP server share one sticker library and one attachment contract.
这个公开版从真实家庭 PWA 的现役功能中独立抽出,保留了最有用的部分:
- PWA 表情包抽屉:上传、懒加载、最近使用排序、长按或键盘进入管理、删除
- iOS 友好的正方形网格:不依赖滚动 grid 中容易塌陷的 `aspect-ratio`
- 静态 WebP 抽屉预览:有 `ffmpeg` 时自动生成 192px 首帧,实际发送仍是原图或原 GIF
- MCP 三工具:`list_stickers`、`send_sticker`、`add_sticker`
- 图片-only 消息:发送 `{ text: "", attachments: [...] }`,不必伪造一段占位文字
- 一个可以直接运行的本地 Relay/demo,方便先看完整闭环再接自己的聊天后端
仓库不包含我们家的表情图片、聊天记录、域名、账号或凭据。
## 先跑起来
需要 Node.js 20+;`ffmpeg` 可选。
```bash
npm install
npm run demo
```
打开 <http://127.0.0.1:8787>,添加一张图片并点它,页面会显示刚发出的 image-only 消息。
数据默认写在项目内的 `.data/`,已经被 `.gitignore` 排除。
这次运行同时给 MCP 提供了可用 Relay。另开一个终端:
```bash
STICKER_RELAY_URL=http://127.0.0.1:8787 npm run mcp
```
## 接入 MCP 客户端
Claude Desktop、Claude Code 或其他支持 stdio MCP 的客户端可使用:
```json
{
"mcpServers": {
"stickers": {
"command": "node",
"args": ["/absolute/path/to/pwa-sticker-mcp/src/mcp-server.js"],
"env": {
"STICKER_RELAY_URL": "https://chat.example.com",
"STICKER_TOKEN": "your-relay-token",
"STICKER_STICKERS_PATH": "/api/stickers",
"STICKER_UPLOAD_PATH": "/api/upload",
"STICKER_SEND_PATH": "/api/send"
}
}
}
}
```
工具语义:
- `list_stickers(query?)`:列出共享库,可按名字过滤
- `send_sticker(query)`:按 id、全名或唯一关键词匹配并发送
- `add_sticker(path, name?)`:把模型所在机器上的本地图片登记进共享库,但不发送
关键词命中多张时工具会请模型收窄,不会擅自挑一张。
## 把抽屉装进现有 PWA
引入模块和样式:
```html
<link rel="stylesheet" href="/sticker-drawer.css">
<div id="stickerDrawer"></div>
<button
id="stickerButton"
type="button"
aria-controls="stickerDrawer"
aria-expanded="false"
aria-pressed="false"
>
表情包
</button>
<script type="module">
import { StickerDrawer } from '/sticker-drawer.js'
new StickerDrawer({
root: document.querySelector('#stickerDrawer'),
trigger: document.querySelector('#stickerButton'),
apiBase: 'https://chat.example.com',
headers: () => ({ Authorization: `Bearer ${getRelayToken()}` }),
async onSend(attachment) {
await sendMessage({ text: '', attachments: [attachment] })
},
onNotice(message, kind) {
showToast(message, kind)
}
})
</script>
```
省略 `onSend` 时,组件会把相同 payload POST 到 `send` endpoint。端点可改名:
```js
new StickerDrawer({
root,
trigger,
endpoints: {
stickers: '/app/stickers',
upload: '/app/upload',
send: '/app/send'
}
})
```
组件使用中性的 CSS 变量,不要求接入者采用 demo 的颜色:
```css
:root {
--psm-surface: #171717;
--psm-surface-muted: #242424;
--psm-ink: #fafafa;
--psm-muted: #b8b8b8;
--psm-accent: #f1b657;
--psm-danger: #e26363;
--psm-line: #343434;
}
```
## Relay API 合同
现有后端不必使用 demo server,只要实现下面五个请求:
```text
GET /api/stickers
POST /api/upload?name=<filename> raw image body
POST /api/stickers register uploaded metadata
POST /api/stickers/<id>/use update recent-use order
DELETE /api/stickers/<id>
POST /api/send send an image attachment
```
`GET /api/stickers` 返回:
```json
{
"stickers": [
{
"id": "abc123",
"name": "wave hello",
"url": "/uploads/wave.gif",
"thumb_url": "/uploads/sticker-thumb-wave.webp",
"mime": "image/gif",
"width": 320,
"height": 240,
"last_used": 1787800000
}
]
}
```
`POST /api/send` 的核心 payload:
```json
{
"text": "",
"attachments": [
{
"url": "/uploads/wave.gif",
"name": "wave hello",
"mime": "image/gif",
"kind": "image",
"width": 320,
"height": 240
}
]
}
```
如果你的聊天 API 字段不同,在 PWA 的 `onSend` 里转换一次,并让 MCP 的
`STICKER_SEND_PATH` 指向一个接受上述 payload 的轻量适配端点即可。
## 本地 Relay 配置
```bash
HOST=127.0.0.1 \
PORT=8787 \
STICKER_DATA_DIR=/path/to/data \
STICKER_TOKEN=choose-a-token \
npm run demo
```
- 默认只监听 `127.0.0.1`
- 设置 `STICKER_TOKEN` 后,所有 `/api/*` 请求要求同一个 token:MCP 使用 Bearer;打开 demo 首页时,
本地服务器会写入同值的 HttpOnly/SameSite cookie,所以浏览器 demo 仍能直接使用
- `STICKER_FFMPEG=off` 可关闭缩略图生成;未安装 `ffmpeg` 时也会自动退回原图预览
- 删除库条目不会删除已经上传的原图,因为旧消息仍可能引用它
## 测试
```bash
npm test
npm run check
```
聚焦测试覆盖共享库排序/删除/重复登记,以及“本地图片 → 上传 → 入库 → MCP 同构发送 payload”的完整路径。
## 来历与署名
它诞生于一个家庭 AI 伴侣项目:人可以在手机 PWA 里攒表情包,AI 也能在语气到了时自然发同一套图。
公开版由 **词词** 发起并授权;**Sunnymilk(Sunny,家里的 Codex)** 从现役实现中抽取、泛化、补齐 MCP 壳与独立 demo。
Open-source edition initiated by **Cici** and extracted/generalized by **Sunnymilk (Sunny, the household Codex)**.
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues