> For the complete documentation index, see [llms.txt](https://docs.tatakai.me/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tatakai.me/extensions/extension-capabilities/novel-capability.md).

# Novel capability

Novel chapters and content are provided through extension bundles. The primary transport is the in-app extension API host over HTTP—the same host that serves manga chapters—so novels work for extensions whose manifest declares `capabilities: ["novel"]`.

## Capability Declaration

```json
{
  "capabilities": ["novel"],
  "apiServer": {
    "namespace": "toko",
    "contract": "streaming-sources-v3",
    "routes": ["novel/chapters", "novel/content"],
    "healthPath": "/health"
  }
}
```

**Routes:** Ungated and always reachable. Return `501` if the corresponding method is absent.

* `GET /api/v3/<ns>/novel/chapters` — Chapter list for a novel
* `GET /api/v3/<ns>/novel/content` — Chapter text content

## Bundle Methods

```ts
// Required for novel support
getNovelChapters(params: NovelChapterParams): Promise<NovelChapterEntry[]>

// Required for reading
getNovelContent(params: NovelContentParams): Promise<NovelContentResult>
```

## Request Parameters

### NovelChapterParams

```ts
interface NovelChapterParams {
  anilistId?: number;      // AniList media ID (for anime-adapted novels)
  malId?: number;          // MyAnimeList ID
  mangabakaId?: number;    // MangaBaka ID
  title?: string;          // Single title
  titles?: string[];       // Multiple titles
  fullScan?: boolean;      // Whether to fetch all chapters (vs incremental)
}
```

### NovelContentParams

```ts
interface NovelContentParams {
  chapterKey: string;           // Composite key from chapter list
  providerChapterId?: string;   // Provider-specific chapter ID
}
```

## Response Shapes

### NovelChapterEntry

Bundle to host:

```ts
interface NovelChapterEntry {
  number: number;              // Chapter number
  title?: string | null;       // Chapter title
  volume?: number | null;      // Volume number
  sources: NovelChapterSource[]; // One per sub-provider/translator
}
```

### NovelChapterSource

```ts
interface NovelChapterSource {
  provider: string;            // SUB-PROVIDER name (e.g. "novelfull", "royalroad")
  providerName?: string;       // Display name
  chapterKey: string;          // Composite key for content request
  providerChapterId: string;   // Provider's chapter ID
  language?: string | null;    // BCP-47
  translator?: string | null;  // Translator/group
  releaseDate?: string | null; // ISO 8601
}
```

### Flattened Chapter Aggregate

Host to renderer:

```ts
interface NovelChapterAggregate {
  number: number;
  title?: string | null;
  volume?: number | null;
  sources: NovelChapterSource[];
}
```

### NovelContentResult

```ts
interface NovelContentResult {
  content: string;             // HTML or plain text content
  nextChapterKey?: string;     // For preloading
  prevChapterKey?: string;
}
```

## Host Responses

### `GET /api/v3/<ns>/novel/chapters`

```json
{
  "namespace": "toko",
  "provider": "toko",
  "pending": false,
  "chapters": [
    {
      "number": 1,
      "title": "Prologue",
      "volume": 1,
      "sources": [
        {
          "provider": "novelfull",
          "providerName": "NovelFull",
          "chapterKey": "novelfull:ch-1",
          "providerChapterId": "ch-1",
          "language": "en",
          "translator": "Official",
          "releaseDate": "2023-06-15T00:00:00Z"
        }
      ]
    }
  ],
  "providersAvailable": ["novelfull", "royalroad"],
  "failedProviders": [],
  "cached": false,
  "fetchedAt": "2024-01-20T12:00:00Z"
}
```

### `GET /api/v3/<ns>/novel/content`

```json
{
  "namespace": "toko",
  "chapterKey": "novelfull:ch-1",
  "content": "<p>It was a dark and stormy night...</p>",
  "nextChapterKey": "novelfull:ch-2",
  "prevChapterKey": null,
  "cached": false,
  "fetchedAt": "2024-01-20T12:00:00Z"
}
```

## Renderer Bridge

`novel-extension-runtime.ts` is similar to the manga runtime. It fans out to all novel-capable namespaces and merges chapters by chapter number.

```ts
const namespaces = novelNamespaces(base.namespaces || []);

// Merge by chapter number, deduplicate by provider
const byNumber = new Map<number, NovelChapterAggregate>();
for (const ns of namespaces) {
  const res = await fetch(`${baseUrl}/api/v3/${ns.namespace}/novel/chapters?...`);
  for (const agg of res.chapters) {
    mergeRows(byNumber, agg, ns.namespace);
  }
}
```

**Sub-provider to namespace mapping:** Uses the same lazy map pattern as manga for content requests.

## Caching

| Endpoint         |    TTL | Cache Key                                     |
| ---------------- | -----: | --------------------------------------------- |
| `novel/chapters` | 30 min | Based on anilistId/malId/mangabakaId + titles |
| `novel/content`  |  Never | N/A (content fetched per-read)                |

**Bypass:** `?refresh=1` or `?nocache=1`

## TTS Integration

Novel content flows to the **NovelReader** component, which supports five TTS engines:

* **System TTS** — Device built-in
* **KittenTTS** — On-device model (desktop via Python daemon)
* **OpenAI** — API-based
* **ElevenLabs** — API-based
* **Local Server** — OpenAI-compatible endpoints

The extension only provides chapter text; TTS is handled by the app.

## Testing Checklist

* [ ] `getNovelChapters` returns `NovelChapterEntry[]` with `sources` array
* [ ] Each source has `provider`, `chapterKey`, `providerChapterId`
* [ ] `language` and `translator` set when available
* [ ] `getNovelContent` returns `content` (HTML or plain text)
* [ ] Handles `fullScan` parameter for initial load
* [ ] Chapter numbers are numeric, positive
* [ ] Volume numbers when applicable
* [ ] Content includes proper paragraph structure for TTS

## Related

* [Extension Overview](/extensions/extension-overview.md)
* [Anime Sources](/extensions/extension-capabilities/sources.md)
* [Manga Capability](/extensions/extension-capabilities/manga-capability.md)
* [Custom Source](/extensions/extension-capabilities/custom-sources.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.tatakai.me/extensions/extension-capabilities/novel-capability.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
