Blinko 1.8.8 API 使用详解

Blinko 1.8.8 REST(tRPC,101 路径)与 MCP(10 工具)双通道实测:JWT 鉴权、笔记增删改查与分享、用户接口泄露口令哈希的安全教训、MCP 会话握手实录、错误码 415/406/404/405 排错与 FAQ。

BlinkoBlinko笔记2026/11/01

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 的现实通道是两条:

  1. 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"}
  1. 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创建内容contenttype:blinko(默认,灵感碎片)/note(长文)/todo(任务);正文中 # 即标签
updateBlinko更新笔记id、contentisArchived/isTop/isShare/isRecycle 状态位
deleteBlinko批量删除ids(数字数组)—
createComment给笔记加评论content、noteIdguestName(访客署名)
webSearch联网搜索query依赖 Tavily 密钥配置
webExtra抓取网页正文urls(数组)基于 Jina 爬虫
createScheduledTask创建 AI 定时任务name、prompt、scheduleschedule 为 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_TYPEcurl -d 默认 application/x-www-form-urlencoded,接口只收 JSON加 -H 'Content-Type: application/json',实测返回 {"token":...}
POST /mcp 通知 → 406MCP 协议要求同时接受 JSON 与事件流加 -H 'Accept: application/json, text/event-stream',实测返回 202
GET /api/rss → 404RSS 路由必须带 userId 与格式后缀改用 /api/rss/1/atom?row=20 或 /api/rss/1/rss
GET /mcp → 405Streamable HTTP 端点只接受 POST用 POST 发起 initialize
GET /api/mcp → 404MCP 服务挂在根路径而非 API 前缀下访问 /mcp
GET /api-doc → 301Swagger UI 重定向到规范地址浏览器/curl -L 跟随即可
GET config/ai → 400 BAD_REQUESTtype 为必填枚举?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 1.8.8 API 使用详解 - Quzuo