DEV Community

Programming with Jack Chew
Programming with Jack Chew

Posted on Originally published at linkdigest.dev

Reading links your agent can't open: Xiaohongshu, Douyin, TikTok, YouTube and X as an MCP tool

Originally published at linkdigest.dev, where I build this.

The problem, in one paragraph

Paste a Xiaohongshu, Douyin, TikTok, YouTube or X link into an AI agent and it fetches the URL, gets an app-download shell or a login wall, and tells you there is nothing there. It is not wrong. The content of those posts is video, images, and text printed inside images, behind tokenised share links — none of it is in the HTML a fetch returns. Strip the <script> tags from a real Xiaohongshu note and 264 characters of navigation are left.

What LinkDigest does about it

LinkDigest is a hosted reader: one call turns the link into text an LLM can use — a transcript with timecodes, the on-screen text, a description and OCR of every image, the caption and metadata — as Markdown or JSON. It is an MCP server (claude mcp add --transport http linkdigest https://linkdigest.dev/mcp --header "Authorization: Bearer $KEY"), a REST API, and a web console. Measured on a 17-image Xiaohongshu note: 17 images described and read, 381 on-screen text fragments, 13 key points, 119 seconds. Three digests are free, no card.

What it does not do, stated up front: Bilibili refuses our server's address (HTTP 412), Instagram is wired but not verified, Facebook is out of scope. A digest that could not read part of a post says so in a degraded field instead of returning a thin result quietly — and a digest that read nothing costs nothing.


Your agent is good at reading. It just can't open half the internet.

Hand Claude Code a Xiaohongshu link from a design review, a Douyin video a colleague sent, a TikTok your competitor posted — and it fetches the URL, gets an app-download shell, and tells you it couldn't find anything useful. It isn't wrong. There genuinely is nothing there.

[Diagram: The same URL returns an app-download shell to a plain fetch, and the actual note to LinkDigest.]

This is a post about the second box: how to call it, and three things people actually do with it.

The agent calls it, not you

One command:

claude mcp add --transport http linkdigest \
  https://linkdigest.dev/mcp \
  --header "Authorization: Bearer ld_live_..."
Enter fullscreen mode Exit fullscreen mode

That registers one tool, digest_url(url, format, job_id). Only url is required.

The part that matters is what happens next: you never mention it again. The tool description tells the agent to call it whenever it meets a social link it cannot read, so the agent reaches for it on its own, mid-task, the way it reaches for grep.

[Diagram: The agent calls the MCP server, which calls the engine, and the transcript returns to the agent.]

Cursor takes the same server in ~/.cursor/mcp.json:

{
  "mcpServers": {
    "linkdigest": {
      "url": "https://linkdigest.dev/mcp",
      "headers": { "Authorization": "Bearer ld_live_..." }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

What actually comes back

Not a summary. The parts:

curl -X POST https://linkdigest.dev/api/v1/digest \
  -H "Authorization: Bearer ld_live_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://v.douyin.com/..."}'
Enter fullscreen mode Exit fullscreen mode

A real response, trimmed — this is a Douyin video, run against production while writing this paragraph:

platform           'douyin'
author             '逸蒙的赛博空间'
posted_at          '2026-08-27'
transcript_source  'asr'
cached             True   credits 0
degraded           []
transcript[0]      {"t": 0, "text": "谁能想到,就这么一个丑萌小玩意儿…"}
key_points[0]      "该AI产品名为Tolen,主打「记住你」的核心功能…"
Enter fullscreen mode Exit fullscreen mode

Fifteen fields in total: platform, author, title, posted_at, caption, transcript, ocr_text, images, key_points, raw_markdown, source_url, transcript_source, degraded, plus cached and credits.

Two of those are worth pointing at.

transcript_source says how you got the words — native_captions, asr, or none. Captions the platform already had are exact; speech recognition is not. A pipeline that treats those identically will eventually quote a mis-heard number back at someone as fact.

degraded is a list of what didn't fully work, in plain words. Empty on a clean run. It exists because of a specific bug: a note was digested during a provider rate-limit storm, every vision batch failed, the per-batch handler swallowed each one, and the job returned images: 0, ocr: 0 — a well-formed, completely empty result, which was then cached for thirty days. Nothing downstream could tell an empty answer from an easy one. Now it can.

The pipeline, and why some links take two calls

[Diagram: Four stages: resolve, fetch, read, structure.]

read is where the time goes, because it is the only stage that has to watch or listen to anything. Measured: a Xiaohongshu note with images takes one to two minutes; a YouTube video with captions about two and a half.

That is longer than one HTTP request should wait, so the API hands the work back:

POST /api/v1/digest
→ 202 {"pending":true,"jobId":"abc…","poll":"/api/v1/digest/abc…","retryAfter":15}
Enter fullscreen mode Exit fullscreen mode

Collect it with the same key at that poll path — 202 while it runs, 200 with the digest when it's done. Over MCP you don't do any of this by hand: the tool tells the agent to call digest_url again with the job_id and no url, and the agent does.

Three things people build with it

1. The agent that reads the link itself

The one that needs no code. A teammate drops a Douyin link in an issue; the agent working that issue reads it without anyone transcribing anything. This is the whole reason the tool description is written the way it is — it is instructions for a machine deciding whether to reach for a tool, not marketing copy.

2. A list of links becomes a table

Fifty URLs in, fifty rows out — one POST each, or the Apify Actor if you'd rather not write the loop. The dataset has a row per link with the transcript, the on-screen text and the key points already separated.

Worth knowing what you're joining: there is real demand for exactly this shape of work. A single competing Douyin scraper on the Apify Store has over 1.5 million runs. Almost all of them return metadata — view counts, captions, author handles. Very few return what was actually said.

3. Watching what a competitor publishes

The same loop on a schedule. A creator's video output becomes searchable text over time, so "when did they first mention pricing" is a grep instead of an afternoon of watching.

This is the use case where transcript_source earns its place again. If you're going to quote a competitor's claim back to your own team, it matters whether a human captioned it or a model guessed at it.

What it costs, and why it isn't flat

A digest is not a unit of cost. Measured against real runs:

[Diagram: Measured cost per digest: a web article is a fraction of a cent, a nineteen-minute video is nine cents.]

Which is why pricing counts the two things that drive that spread — images to describe, minutes to transcribe — rather than counting requests. One credit covers a typical post and its first six images; each further six images adds one, and each minute of media adds two.

Cached links are free and never counted. Anything anyone has ever digested comes back in about a second, for nobody's credits.

Where it stops

The honest table, because finding out after you've wired something in is worse than knowing now:

  • Works: Xiaohongshu, Douyin, TikTok, YouTube, X, ordinary web pages.
  • Bilibili returns HTTP 412 to a datacenter address. Needs a residential proxy.
  • Instagram and Facebook need a logged-in session for most posts.

Three free digests, no card, if you want to check any of that yourself.


The platform-by-platform detail — what each one returns and what breaks — is in What it actually takes to read a Xiaohongshu post from a server, and per platform on the coverage pages.

Top comments (0)