Blinko 1.8.8 API 使用详解
Blinko 1.8.8 REST(tRPC,101 路径)与 MCP(10 工具)双通道实测:JWT 鉴权、笔记增删改查与分享、用户接口泄露口令哈希的安全教训、MCP 会话握手实录、错误码 415/406/404/405 排错与 FAQ。
Blinko 1.8.8 API 使用详解
目标:把 Blinko 的两条程序化通道——REST 接口与 MCP 服务——用实测会话完整走一遍:登录取令牌、笔记增删改查与分享、标签与公开接口、配置与 AI 域、MCP 握手与工具调用,并实录 415/406/404/405 四类错误码的根因与修复。本文基于真实运行的 Blinko 1.8.8 实例,所有命令与回显逐条照抄实测会话。环境搭建见《Blinko Docker Compose 部署指南》,配置项含义见《Blinko 1.8.8 配置参数详解》。
一、环境信息
| 项目 | 值 |
|---|---|
| 操作系统 | Ubuntu 26.04.1 LTS(x86_64) |
| 应用版本 | Blinko 1.8.8(REST 与 MCP 双通道) |
| 数据库 | PostgreSQL 14.24 |
| 服务地址 | 服务内部监听 1111,对外发布 8141,本文所有示例访问 http://localhost:8141 |
| 管理员 | admin / q1w2e3r4(role=superadmin) |
| 接口规格 | OpenAPI 3.1.0,标题 "Blinko TRPC API",共 101 个路径 |
| MCP 端点 | POST /mcp(Streamable HTTP,serverInfo=blinko-mcp-server 1.0.0) |
| 取证时间 | 2026-11-01(UTC 会话) |
二、定篇理由与 API 形态
选型阶段容易先入为主地找"Blinko 的命令行工具"——它没有独立 CLI。官方的一键安装脚本 install.sh 只是把发行版的启动命令包了一层壳,日常运维与自动化集成没有任何官方命令行可用。程序化操作 Blinko 的现实通道是两条:
- REST 接口:服务端暴露 tRPC 风格的 HTTP API(路径形如
/api/v1/<域>/<方法>),并自带 OpenAPI 规格。实测拉取并解析规格:
curl -s http://localhost:8141/api/openapi.json | python3 -c "import json,sys; d=json.load(sys.stdin); print('openapi', d['openapi'], '| title:', d['info']['title'], '| paths:', len(d['paths']))"
openapi 3.1.0 | title: Blinko TRPC API | paths: 101
101 个路径覆盖笔记、标签、用户、配置、AI、分享、Webhook 等全部功能域,认证方式为 Bearer JWT。Swagger 调试页挂在 /api-doc,直接访问会重定向:
curl -sI http://localhost:8141/api-doc | head -3
HTTP/1.1 301 Moved Permanently
X-Powered-By: Express
Vary: Origin
301 说明路由存在、只是跳转到规范地址,浏览器跟随即可打开交互式文档。存活探针最省事:
curl -s http://localhost:8141/health
{"status":"ok"}
- MCP 服务:实例自身就是一个 Model Context Protocol 服务端,挂在根路径
/mcp(不是/api/mcp),供 AI 客户端(如各类 Agent)直接读写笔记。第七章实录完整会话。
💡 说明:两条通道共用同一套 JWT 认证与同一份数据,REST 适合脚本与系统集成,MCP 适合把个人笔记库接进 AI 工作流。这也是本专栏用 API 篇替代"CLI 篇"的原因。
三、鉴权:登录取令牌
所有私有接口都要带 Authorization: Bearer <JWT>。令牌从登录接口换取:
curl -s -X POST http://localhost:8141/api/v1/user/login \
-H 'Content-Type: application/json' \
-d '{"name":"admin","password":"q1w2e3r4"}'
{"id":1,"name":"admin","nickname":"admin","role":"superadmin","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic3VwZXJhZG1pbiIsIm5hbWUiOiJhZG1pbiIsInN1YiI6IjEiLCJleHAiOjQ5NDcxMzI0ODksImlhdCI6MTc5MzUzMjQ4OX0.oJ7niXec1m97NdIaDUVh11w-ww_CTeA5CU-WmPI42AI","image":"","loginType":""}
回显解读:role=superadmin 确认管理员身份;token 即 JWT,解出载荷为 {"role":"superadmin","name":"admin","sub":"1","exp":4947132489,"iat":1793532489}——过期时间在2126 年,实质是长期令牌。后文示例统一用先前登录轮次存入 token.txt 的同款令牌(exp=4947131657),两者等价可用。
⚠️ 注意:长期有效的超级管理员令牌等于永久 root 口令,落盘、截图、粘贴到工单都要按密钥对待。对外集成优先用"低权限令牌"(见第六章),泄露后可在设置里重新生成(见配置篇 Access Token 域)。
四、笔记域:写入、列表、详情、分享
4.1 写入/upsert
note/upsert 是创建与更新的统一入口,不带 id 即新建:
curl -s -X POST http://localhost:8141/api/v1/note/upsert -H Content-Type: application/json -H Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic3VwZXJhZG1pbiIsIm5hbWUiOiJhZG1pbiIsInN1YiI6IjEiLCJleHAiOjQ5NDcxMzE2NTcsImlhdCI6MTc5MzUzMTY1N30.9Bu3g5y9qNbVmA_UwWWX7SOlnFbYowva63EUpm9Lxe8 -d '{"content":"# Blinko API 实测\n第二条通过 REST API 写入的碎片笔记,验证 note/upsert 端点。","type":0}'
{"id":10,"type":0,"content":"# Blinko API 实测\n第二条通过 REST API 写入的碎片笔记,验证 note/upsert 端点。","isArchived":false,"isRecycle":false,"isShare":false,"isTop":false,"isReviewed":false,"sharePassword":"","shareEncryptedUrl":null,"shareExpiryDate":null,"shareMaxView":0,"shareViewCount":0,"metadata":null,"accountId":1,"sortOrder":0,"createdAt":"2026-11-01T11:28:09.829Z","updatedAt":"2026-11-01T11:28:09.829Z"}
返回即完整笔记对象:id=10 自增分配;type=0 为碎片笔记(blinko),正文里的 # Blinko API 实测 会被解析为标签;isArchived/isRecycle/isTop 等状态位默认全关。
4.2 分页列表
curl -s -X POST http://localhost:8141/api/v1/note/list -H 'Content-Type: application/json' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic3VwZXJhZG1pbiIsIm5hbWUiOiJhZG1pbiIsInN1YiI6IjEiLCJleHAiOjQ5NDcxMzE2NTcsImlhdCI6MTc5MzUzMTY1N30.9Bu3g5y9qNbVmA_UwWWX7SOlnFbYowva63EUpm9Lxe8' -d '{"page":1,"size":3}'
[{"id":10,"type":0,"content":"# Blinko API 实测\n第二条通过 REST API 写入的碎片笔记,验证 note/upsert 端点。","isArchived":false,"isRecycle":false,"isShare":false,"isTop":false,"isReviewed":false,"sharePassword":"","shareEncryptedUrl":null,"shareExpiryDate":null,"shareMaxView":0,"shareViewCount":0,"metadata":null,"sortOrder":0,"accountId":1,"createdAt":"2026-11-01T11:28:09.829Z","updatedAt":"2026-11-01T11:28:09.829Z","attachments":[],"tags":[],"references":[],"referencedBy":[],"comments":[],"_count":{"comments":0,"histories":0},"isInternalShared":false},{"id":8,"type":0,"content":"# Blinko API 实测\n第一条通过 REST API 写入的碎片笔记,用于验证 note/upsert 端点。","isArchived":false,"isRecycle":false,"isShare":true,"isTop":false,"isReviewed":false,"sharePassword":"","shareEncryptedUrl":"0vm8ektb","shareExpiryDate":null,"shareMaxView":0,"shareViewCount":0,"metadata":null,"sortOrder":0,"accountId":1,"createdAt":"2026-11-01T11:14:25.039Z","updatedAt":"2026-11-01T11:22:50.739Z","attachments":[],"tags":[],"references":[],"referencedBy":[],"comments":[],"_count":{"comments":0,"histories":0},"isInternalShared":false},{"id":9,"type":0,"content":"通过 MCP upsertBlinko 工具写入的笔记,验证 MCP 通道端到端可用。","isArchived":false,"isRecycle":false,"isShare":false,"isTop":false,"isReviewed":false,"sharePassword":"","shareEncryptedUrl":null,"shareExpiryDate":null,"shareMaxView":0,"shareViewCount":0,"metadata":null,"sortOrder":0,"accountId":1,"createdAt":"2026-11-01T11:16:23.262Z","updatedAt":"2026-11-01T11:16:23.262Z","attachments":[],"tags":[],"references":[],"referencedBy":[],"comments":[],"_count":{"comments":0,"histories":0},"isInternalShared":false}]
列表比 upsert 的返回多出关联字段:attachments/tags/references/referencedBy/comments 数组与 _count 计数。三条记录恰好覆盖三种来源——id=8 是本轮会话早前 REST 写入并已开分享(shareEncryptedUrl=0vm8ektb),id=9 是 MCP 通道写入,id=10 是刚写入的这条。
4.3 详情与分享
curl -s -X POST http://localhost:8141/api/v1/note/detail -H 'Content-Type: application/json' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic3VwZXJhZG1pbiIsIm5hbWUiOiJhZG1pbiIsInN1YiI6IjEiLCJleHAiOjQ5NDcxMzE2NTcsImlhdCI6MTc5MzUzMTY1N30.9Bu3g5y9qNbVmA_UwWWX7SOlnFbYowva63EUpm9Lxe8' -d '{"id":10}'
{"id":10,"type":0,"content":"# Blinko API 实测\n第二条通过 REST API 写入的碎片笔记,验证 note/upsert 端点。","isArchived":false,"isRecycle":false,"isShare":false,"isTop":false,"isReviewed":false,"sharePassword":"","shareEncryptedUrl":null,"shareExpiryDate":null,"shareMaxView":0,"shareViewCount":0,"metadata":null,"sortOrder":0,"accountId":1,"createdAt":"2026-11-01T11:28:09.829Z","updatedAt":"2026-11-01T11:28:09.829Z","attachments":[],"tags":[],"references":[],"referencedBy":[],"_count":{"comments":0,"histories":0}}
curl -s -X POST http://localhost:8141/api/v1/note/share -H 'Content-Type: application/json' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic3VwZXJhZG1pbiIsIm5hbWUiOiJhZG1pbiIsInN1YiI6IjEiLCJleHAiOjQ5NDcxMzE2NTcsImlhdCI6MTc5MzUzMTY1N30.9Bu3g5y9qNbVmA_UwWWX7SOlnFbYowva63EUpm9Lxe8' -d '{"id":10,"share":true}'
{"id":10,"type":0,"content":"# Blinko API 实测\n第二条通过 REST API 写入的碎片笔记,验证 note/upsert 端点。","isArchived":false,"isRecycle":false,"isShare":true,"isTop":false,"isReviewed":false,"sharePassword":"","shareEncryptedUrl":"dh7638tl","shareExpiryDate":null,"shareMaxView":0,"shareViewCount":0,"metadata":null,"sortOrder":0,"accountId":1,"createdAt":"2026-11-01T11:28:09.829Z","updatedAt":"2026-11-01T11:28:09.876Z"}
分享接口把 isShare 翻真并分配短链码 dh7638tl(拼到 /share/dh7638tl 即公开页)。shareExpiryDate/shareMaxView 支持限时限次,sharePassword 支持加密分享——带密码的笔记不会进 RSS(见配置篇)。
五、标签与公开域
curl -s http://localhost:8141/api/v1/tags/list -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic3VwZXJhZG1pbiIsIm5hbWUiOiJhZG1pbiIsInN1YiI6IjEiLCJleHAiOjQ5NDcxMzE2NTcsImlhdCI6MTc5MzUzMTY1N30.9Bu3g5y9qNbVmA_UwWWX7SOlnFbYowva63EUpm9Lxe8'
[{"id":1,"name":"Welcome","icon":"🎉","parent":0,"sortOrder":0,"createdAt":"2026-11-01T11:14:17.126Z","updatedAt":"2026-11-01T11:14:17.126Z"},{"id":2,"name":"Attachment","icon":"🔖","parent":1,"sortOrder":0,"createdAt":"2026-11-01T11:14:17.126Z","updatedAt":"2026-11-01T11:14:17.126Z"},{"id":3,"name":"Code","icon":"🪄","parent":1,"sortOrder":0,"createdAt":"2026-11-01T11:14:17.126Z","updatedAt":"2026-11-01T11:14:17.126Z"},{"id":4,"name":"To-Do","icon":"✨","parent":1,"sortOrder":0,"createdAt":"2026-11-01T11:14:17.126Z","updatedAt":"2026-11-01T11:14:17.126Z"},{"id":5,"name":"Multi-Level-Tags","icon":"🏷️","parent":1,"sortOrder":0,"createdAt":"2026-11-01T11:14:17.126Z","updatedAt":"2026-11-01T11:14:17.126Z"}]
标签是树形的:parent=0 为根(Welcome),id=2-5 都挂在 Welcome 下(parent=1),每个标签可带 icon——这就是 Blinko 多级标签能力的接口形态。
两个无需登录的公开接口:
curl -s http://localhost:8141/api/v1/public/site-info
{"id":1,"name":"admin","image":"","description":"","role":"superadmin"}
curl -s http://localhost:8141/api/v1/public/server-version
"1.8.8"
⚠️ 注意:
site-info会把实例管理员用户名暴露给任何匿名访问者,公网部署要有此预期。
RSS 也属公开域,但路径必须带用户与格式,裸 /api/rss 是未定义路由:
curl -s -o /dev/null -w '%{http_code}' http://localhost:8141/api/rss
404
正确用法为 /api/rss/1/atom?row=20 或 /api/rss/1/rss(参数详见配置篇 RSS 一节)。
六、用户域与安全教训
超级管理员令牌可列出全部用户——实测结果给所有自托管用户上了一课:
curl -s http://localhost:8141/api/v1/user/list -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic3VwZXJhZG1pbiIsIm5hbWUiOiJhZG1pbiIsInN1YiI6IjEiLCJleHAiOjQ5NDcxMzE2NTcsImlhdCI6MTc5MzUzMTY1N30.9Bu3g5y9qNbVmA_UwWWX7SOlnFbYowva63EUpm9Lxe8'
[{"id":1,"name":"admin","nickname":"admin","password":"pbkdf2:113f5ee5f2dfb5e20882b00f65388197:23dcf5851b1089c4d18cc44cb22d21b016ccde003a9ed992f226887d4827e65d8696eca39b387def5c11ebb6e0db94cf4d613222ab33e9622b88515d561957d6","image":"","apiToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic3VwZXJhZG1pbiIsIm5hbWUiOiJhZG1pbiIsInN1YiI6IjEiLCJleHAiOjQ5NDcxMzI0ODksImlhdCI6MTc5MzUzMjQ4OX0.oJ7niXec1m97NdIaDUVh11w-ww_CTeA5CU-WmPI42AI","note":0,"role":"superadmin","loginType":"","description":"","linkAccountId":null,"createdAt":"2026-11-01T11:14:17.108Z","updatedAt":"2026-11-01T11:28:09.814Z"}]
⚠️ 注意:回显里
password字段直接吐出了 pbkdf2 哈希(格式pbkdf2:<盐>:<派生密钥>),apiToken字段吐出了长期令牌明文。拿到一个超管令牌再调user/list,就同时得到"可离线爆破的口令哈希 + 可无限复用的 API 令牌"。结论:user/list这类接口绝不允许暴露给不可信网络;若怀疑泄露,立即改密并重新生成 API 令牌。
如果只需要给外部脚本一个受限入口,用低权限令牌接口。实测不带 Content-Type 直接 POST 会被拒:
curl -s -X POST http://localhost:8141/api/v1/user/gen-low-perm-token -H 'Authorization: Bearer <TOKEN>' -d '{}'
{"message":"Unsupported content-type \"application/x-www-form-urlencoded","code":"UNSUPPORTED_MEDIA_TYPE","data":{"code":"UNSUPPORTED_MEDIA_TYPE","httpStatus":415,"path":"users.genLowPermToken"}}
curl 的 -d 默认表单类型,而接口只收 JSON。补上头即成功:
curl -s -X POST http://localhost:8141/api/v1/user/gen-low-perm-token -H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>' -d '{}'
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic3VwZXJhZG1pbiIsIm5hbWUiOiJhZG1pbiIsInN1YiI6IjEiLCJleHAiOjQ5NDcxMzI1MDIsImlhdCI6MTc5MzUzMjUwMn0.bI7OaIiKp-OQ1JgFtDZGj7ccclZSYUfZPZnj1uqIC7I"}
七、配置与 AI 域
用户级键值设置一览(应与 config 表内容一致,详见配置篇第三章):
curl -s http://localhost:8141/api/v1/config/list -H 'Authorization: Bearer <TOKEN>'
{"theme":"system"}
AI 模型配置按槽位查询,type 为必填枚举,缺参时校验错误把合法值全部回显:
curl -s http://localhost:8141/api/v1/config/ai -H 'Authorization: Bearer <TOKEN>'
{"message":"Input validation failed","code":"BAD_REQUEST","data":{"code":"BAD_REQUEST","httpStatus":400,"path":"config.ai"},"issues":[{"code":"invalid_value","values":["mainModel","embeddingModel","voiceModel","rerankModel","imageModel","audioModel"],"path":["type"],"message":"Invalid option: expected one of \"mainModel\"|\"embeddingModel\"|\"voiceModel\"|\"rerankModel\"|\"imageModel\"|\"audioModel\""}]}
curl -s 'http://localhost:8141/api/v1/config/ai?type=mainModel' -H 'Authorization: Bearer <TOKEN>'
null
null 表示该槽位未配置——本实验实例没配 AI 供应商,属正常状态。AI 定时任务与外部 MCP 服务两个列表同理为空:
curl -s http://localhost:8141/api/v1/ai-tasks/list -H 'Authorization: Bearer <TOKEN>'
[]
curl -s http://localhost:8141/api/v1/mcp-servers/list -H 'Authorization: Bearer <TOKEN>'
[]
💡 说明:
mcp-servers/list是"本实例作为客户端去连别人"的清单;下一节的/mcp是"别人连本实例"的服务端,方向相反,勿混。
八、MCP 服务会话实录
MCP(Model Context Protocol)端点挂在根路径 /mcp,传输协议为 Streamable HTTP。握手第一步 initialize,注意必须同时声明两种 Accept:
curl -si -X POST http://localhost:8141/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -H 'Authorization: Bearer $T' -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'
HTTP/1.1 200 OK
X-Powered-By: Express
Vary: Origin
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Mcp-Session-Id
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
mcp-session-id: 186c3ec1-a0de-4cc0-922a-655f2703a568
Date: Sun, 01 Nov 2026 11:28:10 GMT
Transfer-Encoding: chunked
event: message
data: {"result":{"protocolVersion":"2025-03-26","capabilities":{"tools":{"listChanged":true}},"serverInfo":{"name":"blinko-mcp-server","version":"1.0.0"}},"jsonrpc":"2.0","id":1}
响应头里的 mcp-session-id 是后续所有请求必须回带的会话凭证。会话 ID 每次 initialize 都会重新生成,下文实录取自同轮测试中另一次 initialize 所得的会话 dd99b5b1-ec77-4e6f-b328-748632d932e1,命令与回显均为真实。
第二步发 initialized 通知。第一次漏了 Accept 头,直接被协议层拒绝:
curl -s -o /dev/null -w '%{http_code}' -X POST http://localhost:8141/mcp -H 'Content-Type: application/json' -H 'Mcp-Session-Id: dd99b5b1-ec77-4e6f-b328-748632d932e1' -H 'Authorization: Bearer $T' -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
406
补上 Accept: application/json, text/event-stream 后返回 202 Accepted(通知无响应体):
202
握手完成,列出工具(输出做了提取,完整 schema 见下一章):
curl -s -X POST http://localhost:8141/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -H 'Mcp-Session-Id: dd99b5b1-ec77-4e6f-b328-748632d932e1' -H 'Authorization: Bearer $T' -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' | grep -o '"name":"[a-zA-Z]*"'
"name":"searchBlinko"
"name":"upsertBlinko"
"name":"updateBlinko"
"name":"deleteBlinko"
"name":"createComment"
"name":"webSearch"
"name":"webExtra"
"name":"createScheduledTask"
"name":"deleteScheduledTask"
"name":"listScheduledTasks"
真实调用一次写入工具 upsertBlinko:
curl -s -X POST http://localhost:8141/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -H 'Mcp-Session-Id: dd99b5b1-ec77-4e6f-b328-748632d932e1' -H 'Authorization: Bearer $T' -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"upsertBlinko","arguments":{"content":"通过 MCP upsertBlinko 工具写入的笔记,验证 MCP 通道端到端可用。","type":"blinko"}}}'
event: message
data: {"result":{"content":[{"type":"text","text":"{\n \"id\": 11,\n \"type\": 0,\n \"content\": \"通过 MCP upsertBlinko 工具写入的笔记,验证 MCP 通道端到端可用。\",\n \"isArchived\": false,\n \"isRecycle\": false,\n \"isShare\": false,\n \"isTop\": false,\n \"isReviewed\": false,\n \"sharePassword\": \"\",\n \"shareEncryptedUrl\": null,\n \"shareExpiryDate\": null,\n \"shareMaxView\": 0,\n \"shareViewCount\": 0,\n \"metadata\": null,\n \"accountId\": 1,\n \"sortOrder\": 0,\n \"createdAt\": \"2026-11-01T11:28:10.123Z\",\n \"updatedAt\": \"2026-11-01T11:28:10.123Z\"\n}"}],"structuredContent":{"id":11,"type":0,"content":"通过 MCP upsertBlinko 工具写入的笔记,验证 MCP 通道端到端可用。","isArchived":false,"isRecycle":false,"isShare":false,"isTop":false,"isReviewed":false,"sharePassword":"","shareEncryptedUrl":null,"shareExpiryDate":null,"shareMaxView":0,"shareViewCount":0,"metadata":null,"accountId":1,"sortOrder":0,"createdAt":"2026-11-01T11:28:10.123Z","updatedAt":"2026-11-01T11:28:10.123Z"}},"jsonrpc":"2.0","id":3}
新笔记 id=11 落库,且响应同时给出 text 与 structuredContent 双格式——这是 MCP 客户端能直接消费的结构化结果。再验证检索闭环(输出同样有截断展示需要,此处为完整回显):
curl -s -X POST http://localhost:8141/mcp ... -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"searchBlinko","arguments":{"searchText":"MCP 通道"}}}'
event: message
data: {"result":{"content":[{"type":"text","text":"{\n \"success\": true,\n \"notes\": [\n {\n \"id\": 11,\n \"type\": 0,\n \"content\": \"通过 MCP upsertBlinko 工具写入的笔记,验证 MCP 通道端到端可用。\",\n ...\n },\n {\n \"id\": 9,\n \"type\": 0,\n \"content\": \"通过 MCP upsertBlinko 工具写入的笔记,验证 MCP 通道端到端可用。\",\n ...\n }\n ],\n \"message\": \"Found 2 notes matching your search criteria\"\n}"}],"structuredContent":{"success":true,"notes":[{"id":11,"type":0,"content":"通过 MCP upsertBlinko 工具写入的笔记,验证 MCP 通道端到端可用。","isArchived":false,"isRecycle":false,"isShare":false,"isTop":false,"isReviewed":false,"sharePassword":"","shareEncryptedUrl":null,"shareExpiryDate":null,"shareMaxView":0,"shareViewCount":0,"metadata":null,"sortOrder":0,"accountId":1,"createdAt":"2026-11-01T11:28:10.123Z","updatedAt":"2026-11-01T11:28:10.123Z","attachments":[],"tags":[],"references":[],"referencedBy":[],"comments":[],"_count":{"comments":0,"histories":0},"isInternalShared":false},{"id":9,"type":0,"content":"通过 MCP upsertBlinko 工具写入的笔记,验证 MCP 通道端到端可用。","isArchived":false,"isRecycle":false,"isShare":false,"isTop":false,"isReviewed":false,"sharePassword":"","shareEncryptedUrl":null,"shareMaxView":0,"shareViewCount":0,"metadata":null,"sortOrder":0,"accountId":1,"createdAt":"2026-11-01T11:16:23.262Z","updatedAt":"2026-11-01T11:16:23.262Z","attachments":[],"tags":[],"references":[],"referencedBy":[],"comments":[],"_count":{"comments":0,"histories":0},"isInternalShared":false}],"message":"Found 2 notes matching your search criteria"}},"jsonrpc":"2.0","id":4}
关键词 "MCP 通道" 命中 id=11 与 id=9 两条(后者是前一轮会话写入的同文笔记),Found 2 notes matching your search criteria 确认检索闭环可用。两个路径探测收尾:
curl -s -o /dev/null -w '%{http_code}' http://localhost:8141/mcp # GET /mcp
405
curl -s -o /dev/null -w '%{http_code}' http://localhost:8141/api/mcp # GET /api/mcp
404
💡 技术拓展:除 Streamable HTTP 外,发行版内还保留了旧版 SSE 传输路由(
GET /sse+POST /messages),供不支持新协议的老旧 MCP 客户端兼容使用;新集成一律走/mcp。
九、MCP 工具清单
tools/list 返回 10 个工具的完整 JSON Schema,汇总如下:
| 工具 | 用途 | 必填参数 | 关键可选参数 |
|---|---|---|---|
| searchBlinko | 检索笔记/碎片 | 无(searchText 默认空) | page/size/orderBy、type(0/1/2/-1)、isArchived、isRecycle、withoutTag、withFile、withLink、isUseAiQuery(RAG 语义检索)、days(回看天数)、hasTodo |
| upsertBlinko | 创建内容 | content | type:blinko(默认,灵感碎片)/note(长文)/todo(任务);正文中 # 即标签 |
| updateBlinko | 更新笔记 | id、content | isArchived/isTop/isShare/isRecycle 状态位 |
| deleteBlinko | 批量删除 | ids(数字数组) | — |
| createComment | 给笔记加评论 | content、noteId | guestName(访客署名) |
| webSearch | 联网搜索 | query | 依赖 Tavily 密钥配置 |
| webExtra | 抓取网页正文 | urls(数组) | 基于 Jina 爬虫 |
| createScheduledTask | 创建 AI 定时任务 | name、prompt、schedule | schedule 为 cron(如 0 10 * * * 每天 10 点、0 9 * * 1 每周一 9 点) |
| deleteScheduledTask | 删除定时任务 | taskId 或 taskName 二选一 | — |
| listScheduledTasks | 列出定时任务 | 无 | — |
每个工具的 schema 里都有一个标注 internal use, do not pass! 的 token 参数——那是服务端注入用户凭证的内部字段,客户端不要传。createScheduledTask 的 prompt 就是任务触发时 AI 要执行的指令(例如"搜索最新科技新闻并生成摘要笔记"),与配置篇 4.3 节的 AI Scheduled Task worker 对应。
十、排错实录
本轮实测踩到的全部错误码与修复:
| 现象 | 根因 | 修复 |
|---|---|---|
| POST gen-low-perm-token → 415 UNSUPPORTED_MEDIA_TYPE | curl -d 默认 application/x-www-form-urlencoded,接口只收 JSON | 加 -H 'Content-Type: application/json',实测返回 {"token":...} |
| POST /mcp 通知 → 406 | MCP 协议要求同时接受 JSON 与事件流 | 加 -H 'Accept: application/json, text/event-stream',实测返回 202 |
| GET /api/rss → 404 | RSS 路由必须带 userId 与格式后缀 | 改用 /api/rss/1/atom?row=20 或 /api/rss/1/rss |
| GET /mcp → 405 | Streamable HTTP 端点只接受 POST | 用 POST 发起 initialize |
| GET /api/mcp → 404 | MCP 服务挂在根路径而非 API 前缀下 | 访问 /mcp |
| GET /api-doc → 301 | Swagger UI 重定向到规范地址 | 浏览器/curl -L 跟随即可 |
| GET config/ai → 400 BAD_REQUEST | type 为必填枚举 | ?type=mainModel(六枚举之一,见第七章回显) |
规律很清晰:4xx 全是请求形态问题,不是服务故障——Blinko 的错误体统一带 code/httpStatus/path 三元组(如 "path":"users.genLowPermToken"),排错时先看 code,再对照 path 找到对应的 tRPC 过程名。
十一、常见问题 FAQ
Q1:Blinko 有官方命令行工具吗? 没有。install.sh 只是启动包装脚本,程序化操作请走本文两条通道:REST(101 路径)或 MCP(10 工具)。
Q2:令牌放哪管理?
登录返回的 JWT 与 user/list 里的 apiToken 实质同类(长期有效)。建议存进密码管理器或 CI 密钥库,绝不写死进脚本仓库;泄露即重新生成。
Q3:MCP 会话能复用多久? 会话 ID 每次 initialize 重新生成,旧会话失效不报错、直接 404/406 类拒绝;客户端应把 initialize 当作每次连接的起手式,不要缓存 session id 跨进程复用。
Q4:为什么我的 AI 检索工具搜不到内容?
searchBlinko 的 isUseAiQuery=true 走 RAG,需要先在控制台配好嵌入模型并重建索引(见配置篇 4.1/4.3);未配置时保持默认关键词检索即可。
Q5:REST 与 MCP 写同一条笔记,数据有区别吗?
没有。实测 MCP 写入的 id=9/id=11 立刻能被 REST note/list 列出(第四章回显可见 id=9),两通道同库同模型。
十二、参考链接
- 《Blinko 介绍与选型指南》 —— 版本线与选型背景
- 《Blinko Docker Compose 部署指南》 —— 本文实验环境的搭建过程
- 《Blinko 1.8.8 配置参数详解》 —— 配置三层机制与各设置域
- 官方文档 Access Token 篇:https://docs.blinko.space/how-to-use/ai/access-token
- Blinko 源码仓库:https://github.com/blinko-space/blinko
- MCP 协议规范:https://modelcontextprotocol.io/