Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Query and edit a SiYuan knowledge base via its API.
.claude/skills/nousresearch-siyuan/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | 66% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 89% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 195% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 117% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 142% | 0% |
Use the SiYuan kernel API via curl to search, read, create, update, and delete blocks and documents in a self-hosted knowledge base. No extra tools needed -- just curl and an API token.
${HERMES_HOME:-~/.hermes}/.env: SIYUAN_TOKEN=your_token_here SIYUAN_URL=http://127.0.0.1:6806 SIYUAN_URL defaults to http://127.0.0.1:6806 if not set.
All SiYuan API calls are POST with JSON body. Every request follows this pattern:
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/..." \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"param": "value"}'
Responses are JSON with this structure:
json{"code": 0, "msg": "", "data": { ... }}
code: 0 means success. Any other value is an error -- check msg for details.
ID format: SiYuan IDs look like 20210808180117-6v0mkxr (14-digit timestamp + 7 alphanumeric chars).
| Operation | Endpoint | |-----------|----------| | Full-text search | /api/search/fullTextSearchBlock | | SQL query | /api/query/sql | | Read block | /api/block/getBlockKramdown | | Read children | /api/block/getChildBlocks | | Get path | /api/filetree/getHPathByID | | Get attributes | /api/attr/getBlockAttrs | | List notebooks | /api/notebook/lsNotebooks | | List documents | /api/filetree/listDocsByPath | | Create notebook | /api/notebook/createNotebook | | Create document | /api/filetree/createDocWithMd | | Append block | /api/block/appendBlock | | Update block | /api/block/updateBlock | | Rename document | /api/filetree/renameDocByID | | Set attributes | /api/attr/setBlockAttrs | | Delete block | /api/block/deleteBlock | | Delete document | /api/filetree/removeDocByID | | Export as Markdown | /api/export/exportMdContent |
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/search/fullTextSearchBlock" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"query": "meeting notes", "page": 0}' | jq '.data.blocks[:5]'
Query the blocks database directly. Only SELECT statements are safe.
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/query/sql" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"stmt": "SELECT id, content, type, box FROM blocks WHERE content LIKE '\''%keyword%'\'' AND type='\''p'\'' LIMIT 20"}' | jq '.data'
Useful columns: id, parent_id, root_id, box (notebook ID), path, content, type, subtype, created, updated.
Returns block content in Kramdown (Markdown-like) format.
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/block/getBlockKramdown" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"id": "20210808180117-6v0mkxr"}' | jq '.data.kramdown'
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/block/getChildBlocks" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"id": "20210808180117-6v0mkxr"}' | jq '.data'
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/filetree/getHPathByID" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"id": "20210808180117-6v0mkxr"}' | jq '.data'
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/attr/getBlockAttrs" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"id": "20210808180117-6v0mkxr"}' | jq '.data'
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/notebook/lsNotebooks" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{}' | jq '.data.notebooks[] | {id, name, closed}'
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/filetree/listDocsByPath" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"notebook": "NOTEBOOK_ID", "path": "/"}' | jq '.data.files[] | {id, name}'
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/filetree/createDocWithMd" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "notebook": "NOTEBOOK_ID", "path": "/Meeting Notes/2026-03-22", "markdown": "# Meeting Notes\n\n- Discussed project timeline\n- Assigned tasks" }' | jq '.data'
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/notebook/createNotebook" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "My New Notebook"}' | jq '.data.notebook.id'
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/block/appendBlock" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "parentID": "DOCUMENT_OR_BLOCK_ID", "data": "New paragraph added at the end.", "dataType": "markdown" }' | jq '.data'
Also available: /api/block/prependBlock (same params, inserts at the beginning) and /api/block/insertBlock (uses previousID instead of parentID to insert after a specific block).
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/block/updateBlock" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "id": "BLOCK_ID", "data": "Updated content here.", "dataType": "markdown" }' | jq '.data'
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/filetree/renameDocByID" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"id": "DOCUMENT_ID", "title": "New Title"}'
Custom attributes must be prefixed with custom-:
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/attr/setBlockAttrs" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "id": "BLOCK_ID", "attrs": { "custom-status": "reviewed", "custom-priority": "high" } }'
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/block/deleteBlock" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"id": "BLOCK_ID"}'
To delete a whole document: use /api/filetree/removeDocByID with {"id": "DOC_ID"}. To delete a notebook: use /api/notebook/removeNotebook with {"notebook": "NOTEBOOK_ID"}.
bashcurl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/export/exportMdContent" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"id": "DOCUMENT_ID"}' | jq -r '.data.content'
Common type values in SQL queries:
| Type | Description | |------|-------------| | d | Document (root block) | | p | Paragraph | | h | Heading | | l | List | | i | List item | | c | Code block | | m | Math block | | t | Table | | b | Blockquote | | s | Super block | | html | HTML block |
YYYYMMDDHHmmss-xxxxxxx. Reject anything else.code != 0 in responses before processing data.LIMIT in SQL and pipe through jq to extract only what you need.lsNotebooks.If you prefer a native integration instead of curl, install the SiYuan MCP server:
yaml# In ~/.hermes/config.yaml under mcp_servers: mcp_servers: siyuan: command: npx args: ["-y", "@porkll/siyuan-mcp"] env: SIYUAN_TOKEN: "your_token" SIYUAN_URL: "http://127.0.0.1:6806"
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 10,535 | 9,325 | -11% | 1 | 1 | 0% | 886 | 3,962 | +347% | 0 | 0 | — |
case-02 | pass→pass | 6,801 | 3,075 | -55% | 1 | 1 | 0% | 1,277 | 3,773 | +195% | 0 | 0 | — |
case-03 | pass→pass | 8,916 | 4,002 | -55% | 1 | 1 | 0% | 1,767 | 3,829 | +117% | 0 | 0 | — |
case-04 | pass→pass | 7,793 | 3,593 | -54% | 1 | 1 | 0% | 1,562 | 3,774 | +142% | 0 | 0 | — |
case-05 | pass→pass | 9,603 | 6,545 | -32% | 1 | 1 | 0% | 1,642 | 4,372 | +166% | 0 | 0 | — |
case-06 | pass→pass | 12,045 | 2,633 | -78% | 1 | 1 | 0% | 2,500 | 3,594 | +44% | 0 | 0 | — |
case-07 | pass→pass | 4,681 | 2,621 | -44% | 1 | 1 | 0% | 846 | 3,579 | +323% | 0 | 0 | — |
case-08 | pass→pass | 7,291 | 3,400 | -53% | 1 | 1 | 0% | 1,690 | 3,790 | +124% | 0 | 0 | — |
case-09 | pass→pass | 5,798 | 2,793 | -52% | 1 | 1 | 0% | 1,155 | 3,776 | +227% | 0 | 0 | — |
case-10 | fail→pass | 12,381 | 2,994 | -76% | 1 | 1 | 0% | 2,296 | 3,810 | +66% | 0 | 0 | — |
case-11 | pass→pass | 6,820 | 2,158 | -68% | 1 | 1 | 0% | 1,391 | 3,598 | +159% | 0 | 0 | — |
case-12 | pass→pass | 5,042 | 1,633 | -68% | 1 | 1 | 0% | 1,020 | 3,445 | +238% | 0 | 0 | — |
case-13 | pass→pass | 4,200 | 2,273 | -46% | 1 | 1 | 0% | 890 | 3,571 | +301% | 0 | 0 | — |
case-14 | pass→pass | 6,073 | 2,805 | -54% | 1 | 1 | 0% | 1,181 | 3,664 | +210% | 0 | 0 | — |
case-15 | pass→pass | 7,305 | 2,041 | -72% | 1 | 1 | 0% | 1,421 | 3,542 | +149% | 0 | 0 | — |
case-16 | fail→pass | 9,843 | 2,738 | -72% | 1 | 1 | 0% | 1,948 | 3,684 | +89% | 0 | 0 | — |
case-17 | pass→pass | 6,263 | 2,821 | -55% | 1 | 1 | 0% | 1,282 | 3,687 | +188% | 0 | 0 | — |
case-18 | pass→pass | 9,445 | 4,326 | -54% | 1 | 1 | 0% | 2,113 | 3,867 | +83% | 0 | 0 | — |
case-19 | pass→pass | 7,868 | 1,778 | -77% | 1 | 1 | 0% | 1,500 | 3,452 | +130% | 0 | 0 | — |
case-20 | pass→pass | 8,002 | 5,000 | -38% | 1 | 1 | 0% | 1,624 | 4,214 | +159% | 0 | 0 | — |
case-21 | pass→pass | 11,952 | 7,633 | -36% | 1 | 1 | 0% | 2,338 | 4,646 | +99% | 0 | 0 | — |
case-22 | pass→pass | 10,239 | 6,739 | -34% | 1 | 1 | 0% | 1,904 | 4,303 | +126% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +9 percentage points is the difference between those two pass rates over the 21 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.