Skip to main content
Glama
README.md
# bilitrack

bilitrack follows people and content on Bilibili: look a user up,
see what they have posted lately — videos, dynamics, and articles —
check who they follow, and read the comment threads under a video,
dynamic, or article, including subtitle text for a video when you ask
for it. Everything is read from Bilibili's public web API.

This package is a clean-room rebuild of the 12-tool BiliStalkerMCP
tool list, written fresh from its public README spec.

## How it talks to the service

Each tool maps onto a public endpoint under
`https://api.bilibili.com/x/...` and returns that endpoint's `data`
payload:

```bash
# Optional: a logged-in session raises limits and unlocks gated reads.
export SESSDATA="your SESSDATA cookie value"
export BILI_JCT="your bili_jct cookie value (optional)"
export BUVID3="your buvid3 cookie value (optional)"
# Optional override (default: https://api.bilibili.com)
export BILIBILI_API_URL="https://api.bilibili.com"
```

The cookies are genuinely optional. With none set, requests go out
cookieless, which Bilibili accepts for most public reads — nothing
fails just because `SESSDATA` is missing. When the API answers with a
non-zero `code`, the call fails with that code and message; an
upstream HTTP error is reported with its status and a body excerpt.

**Username resolution:** tools take `user_id_or_username`. A numeric
value is used as the user's mid directly; anything else is resolved
through user search and only an exact username match is accepted —
a near match is an error, not a guess.

**Subtitle simplification:** with `fetch_subtitles` set,
`get_video_detail` uses the player API to list subtitle tracks and
returns either the track metadata (`subtitle_scope: "metadata"`) or
the joined text of the selected track (by `subtitle_lang`, or the
first track), truncated to `subtitle_max_chars` when given. It does
not stitch multi-part or AI-generated tracks the way a full player
client would.

## Install

```bash
npx @reneturcios/bilitrack search_users '{"keyword":"example"}'
```

Or run the container image:

```bash
docker run -i ghcr.io/cloudcorprecords/bilitrack
```

The container defaults to the MCP server. Pass CLI arguments to use
it as a CLI instead.

## CLI

The CLI mirrors the tools: one subcommand per tool, taking its
arguments as a single JSON object.

```bash
bilitrack search_users '{"keyword":"example","limit":5}'
bilitrack get_user_info '{"user_id_or_username":"12345"}'
bilitrack get_user_snapshot '{"user_id_or_username":"example"}'
bilitrack get_user_videos '{"user_id_or_username":"12345","page":2}'
bilitrack get_video_detail '{"bvid":"BV1xx411c7mD","fetch_subtitles":true,"subtitle_lang":"zh-CN"}'
bilitrack get_content_comments '{"content_type":"video","content_id":"12345","sort":"hot"}'
bilitrack --version
```

Exit codes: `0` on success, `1` on an API or validation error (the
error names the problem), `2` for usage errors.

## MCP server

One bin serves both faces. The `mcp` subcommand starts a stdio MCP
server:

```bash
npx -y @reneturcios/bilitrack mcp
```

MCP client configuration:

```json
{
  "mcpServers": {
    "bilitrack": {
      "command": "npx",
      "args": ["-y", "@reneturcios/bilitrack", "mcp"],
      "env": { "SESSDATA": "your cookie value (optional)" }
    }
  }
}
```

### Tools

All ID parameters are strings in these schemas.

| Tool | Input | What it does |
| --- | --- | --- |
| `search_users` | `keyword` (required), `limit` | Search Bilibili users by keyword. |
| `get_user_snapshot` | `user_id_or_username` (required), `video_limit`, `dynamic_limit`, `article_limit` | Get a combined snapshot of a Bilibili user: profile, videos, dynamics, and articles. |
| `get_user_info` | `user_id_or_username` (required) | Get profile information for a Bilibili user. |
| `get_user_videos` | `user_id_or_username` (required), `page`, `limit` | List a Bilibili user's videos, paged. |
| `search_user_videos` | `user_id_or_username` (required), `keyword` (required), `page`, `limit` | Search within a Bilibili user's videos by keyword. |
| `get_video_detail` | `bvid` (required), `fetch_subtitles`, `subtitle_scope`, `subtitle_page`, `subtitle_cid`, `subtitle_mode`, `subtitle_lang`, `subtitle_max_chars` | Get detail for a Bilibili video by BVID, optionally fetching subtitles. |
| `get_user_dynamics` | `user_id_or_username` (required), `cursor`, `limit`, `dynamic_type` | List a Bilibili user's dynamics (feed posts), paged by cursor. |
| `get_user_articles` | `user_id_or_username` (required), `page`, `limit` | List a Bilibili user's articles, paged. |
| `get_article_content` | `article_id` (required; numeric or `cv`-prefixed) | Get the full content of a Bilibili article. |
| `get_user_followings` | `user_id_or_username` (required), `page`, `limit` | List accounts a Bilibili user follows, paged. |
| `get_content_comments` | `content_type` (required), `content_id` (required), `cursor`, `limit`, `sort` | List comments on a Bilibili video, dynamic, or article. |
| `get_content_comment_replies` | `content_type` (required), `content_id` (required), `root_rpid` (required), `page`, `limit` | List replies under one root comment on Bilibili content. |

`content_type` accepts `video`, `article`, `dynamic`, `audio`,
`album`, `course`, or a numeric Bilibili reply type as a string.

## Development

```bash
npm install
npm run build   # tsc -> dist/
npm test        # node test runner over test/*.test.ts, fake fetch only, no network
npm run dev     # run the CLI from source via tsx
```

## License

MIT — see `LICENSE`.