Back to skills
extension
Category: Development & EngineeringNo API key required

ModelScope 研习社发布skill(优化版)

在无浏览器/CLI/DSW 环境用 access token 把 Markdown 转成富文本 ContentDraft 并发布 ModelScope 研习社文章。token 当 m_session_id Cookie 认证、Markdown→cangjie 节点转换、先草稿后发布。

personAuthor: OceanZhengYanghubOpenAPI

Publish to ModelScope 研习社 (Learn) — headless

Publish a Markdown file as a rich ModelScope 研习社 article with only an access token. Works where there is no browser/Playwright/GUI.

When to use / not use

  • Use when the user wants to create, edit, or publish an article on https://modelscope.cn/learn from a terminal, or when their article renders as literal ## / code fences / |tables| (the markdown was stored as plain text, not converted).
  • Do not use for models, datasets, Studios, or MCP — that is ms-hub.
  • Pasting Markdown source into the web editor does not auto-convert on ModelScope, so do not advise "just paste the .md"; always generate the rich block nodes (the script below does).

Prerequisites

  • ModelScope access token, resolved in this order: --token > $MODELSCOPE_API_KEY > --token-file > $MODELSCOPE_TOKEN default file. Domestic tokens only work on modelscope.cn (tokens are site-scoped).
  • Python 3 (stdlib only — no extra deps).

One-shot flow

# 1) Stage a rich DRAFT (Status=0, safe; prints draft id + edit URL):
python scripts/headless_publish.py --in article.md \
  --title "文章标题" --desc "≤200字简介" \
  --subjects "开发者工具,API" --domain 人工智能
#    → draft id: <ID>
#    → https://modelscope.cn/learn/edit/<ID>

# 2) Open the edit URL in a browser, eyeball the render, fix the .md, then iterate on the SAME id
#    (create/update reuse one draft id — stray drafts are undeletable via token):
python scripts/headless_publish.py --in article.md --id <ID>

# 3) Publish (IRREVERSIBLE; delete is web-console only):
python scripts/headless_publish.py --in article.md --id <ID> --publish --yes

Auth: token as the m_session_id cookie

Authorization: Bearer and ?access_token= do not authorize writes to /api/v1/articles (they return {"Code":10010101003,"Message":"user not logged in"}). The web API authenticates the session cookie only, so send the access token as Cookie: m_session_id=<TOKEN>.

Endpoints (legacy web API, base https://modelscope.cn)

| Method | Path | Purpose | |---|---|---| | POST | /api/v1/articles | Create draft (returns Data.Id, Status=0) | | PUT | /api/v1/articles | Full update — must include Id; omitted fields get cleared | | GET | /api/v1/articles/{id} | Read back; body is at Data.Articles[0], not Data | | PUT | /api/v1/articles/{id}/publish | Publish |

ContentDraft: why Markdown must be converted

The persisted body field is ContentDraft (DingTalk/cangjie rich-JSON). Writing ContentMd / Content is ignored (reads back empty). The wrapper is always ["root", {}, block, block, ...]. Flattening Markdown into plain paragraph nodes stores fine but renders the raw source (visible ##, ``` fences, |…|) — you must emit real block nodes: headings, code, inlineCode, table (cell tag is tc), bullet lists (p + list attr), hr. See references/api-reference.md for the exact node schemas and scripts/headless_publish.py for a working Markdown→ContentDraft converter.

Field mapping & gotchas

  • PUT uses Description for the summary; GET returns it as Desc. Title ≤ 128, Description ≤ 200.
  • Tags is not persisted (reads back empty). Real topic tags live in Subjects (a JSON-array string).
  • PUT /api/v1/articles is a full replacement, not a partial patch.
  • Publishing via token is one-way: DELETE is blocked ("deletion restricted to web console"), and the list/search API ignores every filter (SearchKey/Status/Owner), so you cannot enumerate or clean up your own drafts. Never fire throwaway POST probes; reuse one draft id while iterating.
  • Encoding: build the payload with json.dump(..., ensure_ascii=False) in a Python file; do not push Chinese through inline bash -c / GBK consoles.

Safety defaults

The script creates a draft by default and only publishes with an explicit --publish --yes. Keep it that way: publish is public + irreversible, so stage → verify render → publish.