# Jmcomic

> Search, browse, inspect album-specific or site-wide comments, list favorite folders, browse, add, and delete favorites, and download manga from JMComic (18comic), obtain the latest Android APK from hect0x7/JMComic-APK, and invoke the upstream jm-view-server `jms` command for local reading. Use for manga discovery, ranking, comment analysis, favorites, downloads, post-processing, configuration, requests to download the JMComic APK, requests to start a local or phone-accessible manga reader, and download-then-read workflows.

- Skill: `hect0x7/jmcomic` (Agent Skill, multi-file: 25 files)
- Install (CLI): `npx skillmds@latest add hect0x7/jmcomic`
- Raw SKILL.md: https://api.skillmd.com/api/skills/hect0x7/jmcomic/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: hect0x7 (https://skillmd.com/u/hect0x7)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/hect0x7/jmcomic

---


# JMComic Skill

This skill enables you to interact with JMComic (18comic), a popular manga platform, to search, browse, and download manga content.

## When to Use This Skill

Activate this skill when the user wants to:
- Search for manga by keyword or category
- Browse popular manga rankings (daily, weekly, monthly)
- Read album-specific or site-wide comments and nested replies, including spoiler flags
- List favorite folders, browse saved albums, add an album to favorites, or delete an album from favorites
- Download entire albums or specific chapters (**Returns structured dict with status, paths, and metadata**)
- Get detailed information about a manga album
- Configure download settings (paths, concurrency, proxies)
- Post-process downloaded content (Zip, PDF, LongImage) with **native parameters or `dir_rule`**
- Download the latest Android APK published by `hect0x7/JMComic-APK`
- Run `jms --help` to obtain the current upstream options, then invoke `jms` directly for an existing or newly downloaded manga directory

For APK download, local-reader startup, LAN safety, and download-to-read continuation rules, read
`references/ecosystem.md` before acting.

### 📥 Download Tools Return Structured Data

Both `download_album` and `download_photo` return structured dictionaries:

**`download_album(album_id: str, ctx: Context = None)`** returns:
```python
{
    "status": "success" | "failed",
    "album_id": str,
    "title": str,
    "download_path": str,  # Absolute path to download directory
    "duration": float | None,  # Total elapsed time in seconds
    "image_paths": list[str],  # Actual downloaded or cached image paths
    "export_files": dict[str, list[str]],  # Plugin outputs grouped by suffix
    "task_id": str,        # Dedicated ID for this download invocation
    "log_path": str,       # Absolute path to this task's log file
    "error": str | None
}
```

**`download_photo(photo_id: str, ctx: Context = None)`** returns:
```python
{
    "status": "success" | "failed",
    "photo_id": str,
    "image_count": int,    # Number of actual image paths in this result
    "download_path": str,  # Absolute path to download directory
    "duration": float | None,  # Total elapsed time in seconds
    "image_paths": list[str],  # Actual downloaded or cached image paths
    "export_files": dict[str, list[str]],  # Plugin outputs grouped by suffix
    "task_id": str,        # Dedicated ID for this download invocation
    "log_path": str,       # Absolute path to this task's log file
    "error": str | None
}
```

**Real-time Progress Tracking**: Both methods accept an optional `ctx: Context` parameter (automatically injected by FastMCP). When provided, progress updates are sent via MCP notifications in real-time, allowing AI agents to monitor download progress.

**Persistent Task Logs**: Every download invocation creates an isolated log file and returns its `task_id` and `log_path`, including failed downloads. Logs default to `~/.jmcomic-ai/logs`; set `JM_TASK_LOG_DIR` to use another directory.

**Artifact Handoff**: Use `image_paths` and `export_files` for packaging, sending, or cleanup instead of rescanning the configured directory. `download_path` comes from the completed upstream download result rather than a pre-download path prediction.

**File-only Logging**: Regular `jmcomic`, `jmcomic_ai`, and MCP runtime logs share `~/.jmcomic-ai/jmcomic_ai.log` and are not emitted to stdout or stderr. Set `JM_LOG_PATH` to override the global log file. MCP protocol messages and explicit CLI result output are unaffected.

**Upstream Rich Terminal Progress**: `jmcomic 2.7.4` adds the optional `download_progress` plugin with `log_file` and `terminal_log_lines`. Recommend it only when the user explicitly wants Rich progress in an interactive terminal. Do not enable it for `jmai` MCP/stdio downloads, which already use MCP Context notifications and isolated task logs.

### Album Comments

Use `get_album_comments(album_id: str, page: int = 1)` to read one page of comments. This is a read-only tool; it does not post comments or replies.

```python
{
    "album_id": str,
    "page": int,
    "page_size": int,
    "total": int | None,
    "page_count": int | None,
    "comment_count": int,
    "comments": [
        {
            "comment_id": str | None,
            "album_id": str | None,
            "user_id": str | None,
            "parent_comment_id": str | None,
            "content": str,
            "username": str,
            "nickname": str,
            "is_spoiler": bool,
            "created_at": object,
            "likes": int | None,
            "replies": [...]  # Recursive nested replies
        }
    ]
}
```

Use `get_forum_comments(page: int = 1)` for the latest site-wide comments. It returns the same comment objects and pagination fields without an `album_id` filter; each comment's own `album_id` identifies its source album. This is also read-only.

### Favorites

All favorite tools require an authenticated client. Call `login` in the same MCP session or configure
valid cookies. For HTML queries authenticated only by Cookie, pass `username`; after `login`, it can
be omitted. The API client ignores `username` and always queries the current account. Separate script
processes need authentication in their configuration; they do not inherit an MCP session's login.

**`get_favorite_folders(username: str = "")`** returns:

```python
{"folders": [{"id": str, "name": str}]}
```

The directory can be empty. Use `folder_id="0"` to browse all favorites even if this ID is absent
from the returned directory.

**`browse_favorite_albums(folder_id: str = "0", page: int = 1, order_by: str = "favorite_time", username: str = "")`** returns:

```python
{
    "albums": [{"id": str, "title": str, "tags": list, "cover_url": str}],
    "total_count": int,
    "page": int,
    "folder_id": str,
    "error": str  # Present only for invalid page, folder ID, or sort parameters
}
```

Pagination starts at 1; `folder_id="0"` means all favorites. An empty result retains pagination and
folder metadata. `order_by` accepts `favorite_time` (default) and `update_time`; the favorites list has
its own upstream sort vocabulary, which differs from `browse_albums`. The service does not sort a page
locally. Invalid arguments return an empty list with `error`; authentication and request failures
propagate as tool errors, not empty collections.

**`add_favorite_album(album_id: str)`** returns:

```python
{
    "status": "success" | "error",
    "album_id": str,
    "title": str,   # Album title
    "message": str
}
```

`album_id` accepts a numeric ID, a JM-prefixed ID, or an album URL and is normalized when parsing
succeeds. The album is saved to the account's default favorites placement.
Adding an album that is already saved returns `status="error"` and leaves the saved state unchanged.

**`delete_favorite_album(album_id: str)`** returns the same fields and takes the same `album_id` forms.
Deleting an album that is not saved returns `status="error"` and leaves the saved state unchanged.

Both tools behave identically whichever client implementation the configuration selects, and both return
the album `title`. Name every affected album as `title (ID)`; when `title` is empty or absent, reuse an
earlier search, browse, or detail result, or fetch the name with `get_album_detail(album_id)` or
`python scripts/album_info.py --id ID`.

## Core Capabilities

### 🛠️ Post-Processing

This skill supports advanced post-processing of downloaded manga. It returns structured data including the **output path** of the generated file.

- **📦 Zip Compression**: Pack an entire album or individual chapters into a ZIP file.
- **📄 PDF Conversion**: Merge all images of an album into a single PDF document.
- **🖼️ Long Image Merging**: Combine all pages of a chapter into one continuous long image.

**`post_process(album_id: str, process_type: str, params: dict = None)`** returns:
```python
{
    "status": "success" | "error",
    "process_type": str,  # Process type used
    "album_id": str,  # Album ID processed
    "output_path": str,  # Absolute path to generated file/directory (empty string on error)
    "output_paths": list[str],  # All files actually registered by the upstream plugin
    "is_directory": bool,  # True if output is a directory (e.g., photo-level zip), False on error
    "message": str  # Success/error message
}
```

**All fields are always present**. On error, `output_path` is empty, `output_paths` is empty, and `is_directory` is `False`.

**Output Control**: Use `dir_rule` (a `{"rule": "DSL_STRING", "base_dir": "BASE_PATH"}` dict) for custom output paths. If omitted, files are saved in the configured default directory. The DSL supports `Bd` (base_dir), `Axxx`/`Pxxx` album/photo attributes, and `{attr}` Python format placeholders. When looping over albums into one `base_dir`, include `{Aid}` or `{Atitle}` to avoid overwrites.

For the full set of ZIP/PDF/LongImg × album/photo `dir_rule` examples, see `references/post_process.md`.

This skill provides command-line utilities for JMComic operations. All utilities are Python scripts located in the `scripts/` directory and should be executed using Python.

### Data Structure Notes

Most search and browsing tools (e.g., `search_album`, `browse_albums`) return a consistent structure that supports pagination:

```json
{
  "albums": [ ... ],
  "total_count": 1234
}
```

**`total_count`** provides the total number of items available across all pages, allowing you to calculate the number of remaining pages and decide if further searching is needed.

#### Important: Browse Albums Data Limitations

**`browse_albums`** is the unified tool for browsing albums by `category`, `time_range`, and `order_by` (ranking + category browsing in one interface). Its response is lightweight — each album has only `id`, `title`, `tags`, and `cover_url`, and **no** stats (likes/views/author). To get those, call **`get_album_detail(album_id)`** per album.

`order_by` accepts: `latest` (default), `likes`, `views`, `pictures`, `score`, `comments`. For the full enumeration, use-case recipes (rankings, category browsing, combined queries), and the "top 10 with details" workflow, see `references/browse_albums.md`.


## Configuration Reference


For detailed configuration options, refer to:
- **`references/reference.md`**: Human-readable configuration guide
- **`assets/option_schema.json`**: JSON Schema for validation

Common configuration examples:

```yaml
# Change download directory
dir_rule:
  base_dir: "/path/to/downloads"
  rule: "Bd / Ptitle"

# Adjust concurrency
download:
  threading:
    image: 30  # Max concurrent image downloads
    photo: 5   # Max concurrent chapter downloads

# Set proxy
client:
  async_impl: async_api  # Used by jmcomic native async APIs
  cache: level_option    # Reuse metadata across clients from this option
  postman:
    meta_data:
      proxies:
        http: "http://proxy.example.com:8080"
        https: "https://proxy.example.com:8080"

# Or use system proxy
client:
  postman:
    meta_data:
      proxies: system

# Configure login cookies
client:
  postman:
    meta_data:
      cookies:
        AVS: "your_avs_cookie_value"

# Use plugins
plugins:
  after_album:
    - plugin: zip
      kwargs:
        level: photo
        suffix: zip
        delete_original_file: true
```

## Available Command-Line Tools

The `scripts/` directory provides utility tools for common tasks. All tools support the `--help` flag for detailed usage. The table below summarizes each script; for full per-script examples and feature lists, see `references/scripts.md`.

| Script | Purpose |
| :--- | :--- |
| `doctor.py` | Environment diagnostics: Python version, deps, config status, network/domain checks. |
| `batch_download.py` | Download multiple albums from a list of IDs (CLI or file) with progress/error summary. |
| `download_photo.py` | Download specific chapters/photos without fetching whole albums. |
| `validate_config.py` | Validate `option.yml` and convert between YAML and JSON. |
| `search_export.py` | Search by keyword/ranking/category and export to CSV or JSON (multi-page). |
| `album_info.py` | Query detailed metadata for one or many albums; print or export to JSON. |
| `album_comments.py` | Fetch one page of album comments and recursive replies; print or export to JSON. |
| `forum_comments.py` | Fetch one page of site-wide comments with source album IDs; print or export to JSON. |
| `favorite_folders.py` | List favorite folders as JSON; supports `--username`, `--output`, and `--option`. |
| `favorite_albums.py` | Browse one page of favorites as JSON; supports folder, page, sort, and username filters. |
| `add_favorite_album.py` | Add one favorite and print its structured result as JSON; failures exit non-zero. |
| `delete_favorite_album.py` | Delete one favorite and print its structured result as JSON; failures exit non-zero. |
| `download_covers.py` | Batch download album cover images to a custom output directory. |
| `ranking_tracker.py` | Track day/week/month rankings over time; export snapshots with timestamps. |
| `post_process.py` | Convert downloads to ZIP/PDF/LongImg, with optional encryption and `dir_rule` DSL. |
| `download_latest_apk.py` | Download the latest APK published by `hect0x7/JMComic-APK`; supports optional `output_dir`, `--force`, and `--json` (run `--help` for current usage). |

## Script Parameters ↔ MCP Tools Mapping

The following table clarifies how script CLI parameters map to MCP tools.

| Script | Target Tool | Mapping Level | Notes |
| :--- | :--- | :--- | :--- |
| `search_export.py` | `search_album` / `browse_albums` | Partial | `--keyword` maps to `search_album`; `--ranking` / `--category` maps to `browse_albums`. Ranking is a convenience mode based on `time_range` + configurable sort, not a separate backend API. |
| `post_process.py` | `post_process` | High | `--id`→`album_id`, `--type`→`process_type`, optional flags to `params`. `--dir-rule` + `--base-dir` map to `params.dir_rule`. |
| `album_info.py` | `get_album_detail` | Partial | Batch wrapper over repeated single-album calls; output format is script-defined. |
| `album_comments.py` | `get_album_comments` | High | `--id` maps to `album_id`, `--page` maps to `page`, and the JSON response preserves the MCP result shape. |
| `forum_comments.py` | `get_forum_comments` | High | `--page` maps to `page`, and each returned comment preserves its source `album_id`. |
| `favorite_folders.py` | `get_favorite_folders` | High | `--username` maps to `username`; JSON preserves the MCP result shape. |
| `favorite_albums.py` | `browse_favorite_albums` | High | `--folder-id`, `--page`, `--order-by`, `--username` map to the corresponding tool arguments. |
| `add_favorite_album.py` | `add_favorite_album` | High | `--id` maps to `album_id`; an error result exits non-zero. |
| `delete_favorite_album.py` | `delete_favorite_album` | High | `--id` maps to `album_id`; an error result exits non-zero. |
| `download_covers.py` | `download_cover` | Partial | Batch wrapper over repeated cover calls. |
| `ranking_tracker.py` | `browse_albums` | Partial | Uses time-range/category browse semantics and exports snapshots. |
| `batch_download.py` | `download_album` | Partial | Batch wrapper over repeated calls; prints each result's download path and dedicated log path. |
| `download_photo.py` | `download_photo` | Partial | Batch wrapper over repeated calls; prints each result's download path and dedicated log path. |
| `validate_config.py` | `update_option` (adjacent) | None | Validation/format conversion utility; not a direct MCP tool wrapper. |
| `download_latest_apk.py` | None | None | Reads the public `hect0x7/JMComic-APK` GitHub Release API directly. |

### Mapping Policy

- **MCP tools are the source of truth** for agent-facing contracts (name, args, return structure).
- **Scripts are operational helpers** for local package workflows and may expose different output formatting.
- If strict schema guarantees are required, prefer calling MCP tools directly instead of scripts.

## Important Notes

- **Legal Compliance**: Ensure you have the right to download content
- **Rate Limiting**: The platform may rate-limit requests; adjust threading if needed
- **Storage**: Downloads can be large; ensure sufficient disk space
- **Configuration**: Default config is at `~/.jmcomic/option.yml`
- **Next action**: After a successful manga download, offer to start the local reader for the returned
  `download_path`. Start it automatically only when the user also asked to open, read, or view the result.

## Troubleshooting

- **Connection errors**: Try updating the domain list in client config
- **Slow downloads**: Reduce threading concurrency
- **Scrambled images**: Ensure `download.image.decode` is set to `true`

