# Danbooru MCP Tag Validator — User Guide

This guide explains how to integrate and use the `danbooru-mcp` server with an LLM to generate valid, high-quality prompts for Illustrious / Stable Diffusion models trained on Danbooru data.

---

## Table of Contents

1. [What is this?](#what-is-this)
2. [Quick Start](#quick-start)
3. [Tool Reference](#tool-reference)
   - [search_tags](#search_tags)
   - [validate_tags](#validate_tags)
   - [suggest_tags](#suggest_tags)
4. [Prompt Engineering Workflow](#prompt-engineering-workflow)
5. [Category Reference](#category-reference)
6. [Best Practices](#best-practices)
7. [Common Scenarios](#common-scenarios)
8. [Troubleshooting](#troubleshooting)

---

## What is this?

Illustrious (and similar Danbooru-trained Stable Diffusion models) uses **Danbooru tags** as its prompt language.  
Tags like `1girl`, `blue_hair`, `looking_at_viewer` are meaningful because the model was trained on images annotated with them.

The problem: there are hundreds of thousands of valid Danbooru tags, and misspelling or inventing tags produces no useful signal — the model generates less accurate images.

**This MCP server** lets an LLM:
- **Search** the full tag database for tag discovery
- **Validate** a proposed prompt's tags against the real Danbooru database
- **Suggest** corrections for typos or near-miss tags

The database contains **292,500 tags**, all with ≥10 posts on Danbooru — filtering out one-off or misspelled entries.

---

## Quick Start

### 1. Add to your MCP client (Claude Desktop example)

**Using Docker (recommended):**
```json
{
  "mcpServers": {
    "danbooru-tags": {
      "command": "docker",
      "args": ["run", "--rm", "-i", "danbooru-mcp:latest"]
    }
  }
}
```

**Using Python directly:**
```json
{
  "mcpServers": {
    "danbooru-tags": {
      "command": "/path/to/danbooru-mcp/.venv/bin/python",
      "args": ["/path/to/danbooru-mcp/src/server.py"]
    }
  }
}
```

### 2. Instruct the LLM

Add a system prompt telling the LLM to use the server:

```
You have access to the danbooru-tags MCP server for validating Stable Diffusion prompts.
Before generating any final prompt:
1. Use validate_tags to check all proposed tags are real Danbooru tags.
2. Use suggest_tags to fix any invalid tags.
3. Only output the validated, corrected tag list.
```

---

## Tool Reference

### `search_tags`

Find tags by name using full-text / prefix search.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `query` | `string` | *required* | Search string. Trailing `*` added automatically for prefix match. Supports FTS5 syntax. |
| `limit` | `integer` | `20` | Max results (1–200) |
| `category` | `string` | `null` | Optional filter: `"general"`, `"artist"`, `"copyright"`, `"character"`, `"meta"` |

**Returns:** List of tag objects:
```json
[
  {
    "name": "blue_hair",
    "post_count": 1079925,
    "category": "general",
    "is_deprecated": false
  }
]
```

**Examples:**

```
Search for hair colour tags:
  search_tags("blue_hair")
  → blue_hair, blue_hairband, blue_hair-chan_(ramchi), …

Search only character tags for a Vocaloid:
  search_tags("hatsune", category="character")
  → hatsune_miku, hatsune_mikuo, hatsune_miku_(append), …

Boolean search:
  search_tags("hair AND blue")
  → tags matching both "hair" and "blue"
```

**FTS5 query syntax:**

| Syntax | Meaning |
|--------|---------|
| `blue_ha*` | prefix match (added automatically) |
| `"blue hair"` | phrase match |
| `hair AND blue` | both terms present |
| `hair NOT red` | exclusion |

---

### `validate_tags`

Check a list of tags against the full Danbooru database. Returns three groups: valid, deprecated, and invalid.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `tags` | `list[string]` | Tags to validate, e.g. `["1girl", "blue_hair", "sword"]` |

**Returns:**
```json
{
  "valid":      ["1girl", "blue_hair", "sword"],
  "deprecated": [],
  "invalid":    ["blue_hairs", "not_a_real_tag"]
}
```

| Key | Meaning |
|-----|---------|
| `valid` | Exists in Danbooru and is not deprecated — safe to use |
| `deprecated` | Exists but has been deprecated (an updated canonical tag exists) |
| `invalid` | Not found — likely misspelled, hallucinated, or too niche (<10 posts) |

**Important:** Always run `validate_tags` before finalising a prompt. Invalid tags are silently ignored by the model but waste token budget and reduce prompt clarity.

---

### `suggest_tags`

Autocomplete-style suggestions for a partial or approximate tag. Results are sorted by post count (most commonly used first). Deprecated tags are **excluded**.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `partial` | `string` | *required* | Partial tag or rough approximation |
| `limit` | `integer` | `10` | Max suggestions (1–50) |
| `category` | `string` | `null` | Optional category filter |

**Returns:** Same format as `search_tags`, sorted by `post_count` descending.

**Examples:**

```
Fix a typo:
  suggest_tags("looking_at_vewer")
  → ["looking_at_viewer", …]

Find the most popular sword-related tags:
  suggest_tags("sword", limit=5, category="general")
  → sword (337,737), sword_behind_back (7,203), …

Find character tags for a partial name:
  suggest_tags("miku", category="character")
  → hatsune_miku (129,806), yuki_miku (4,754), …
```

---

## Prompt Engineering Workflow

This is the recommended workflow for an LLM building Illustrious prompts:

### Step 1 — Draft

The LLM drafts an initial list of conceptual tags based on the user's description:

```
User: "A girl with long silver hair wearing a kimono in a Japanese garden"

Draft tags:
  1girl, silver_hair, long_hair, kimono, japanese_garden, cherry_blossoms,
  sitting, looking_at_viewer, outdoors, traditional_clothes
```

### Step 2 — Validate

```
validate_tags([
  "1girl", "silver_hair", "long_hair", "kimono", "japanese_garden",
  "cherry_blossoms", "sitting", "looking_at_viewer", "outdoors",
  "traditional_clothes"
])
```

Response:
```json
{
  "valid": ["1girl", "long_hair", "kimono", "cherry_blossoms", "sitting",
            "looking_at_viewer", "outdoors", "traditional_clothes"],
  "deprecated": [],
  "invalid": ["silver_hair", "japanese_garden"]
}
```

### Step 3 — Fix invalid tags

```
suggest_tags("silver_hair", limit=3)
→ [{"name": "white_hair", "post_count": 800000}, ...]

suggest_tags("japanese_garden", limit=3)
→ [{"name": "garden", "post_count": 45000}, 
   {"name": "japanese_clothes", "post_count": 12000}, ...]
```

### Step 4 — Finalise

```
Final prompt:
  1girl, white_hair, long_hair, kimono, garden, cherry_blossoms,
  sitting, looking_at_viewer, outdoors, traditional_clothes
```

All tags are validated. Prompt is ready to send to ComfyUI.

---

## Category Reference

Danbooru organises tags into five categories. Understanding them helps scope searches:

| Category | Value | Description | Examples |
|----------|-------|-------------|---------|
| **general** | `0` | Descriptive tags for image content | `1girl`, `blue_hair`, `sword`, `outdoors` |
| **artist** | `1` | Artist/creator names | `wlop`, `natsuki_subaru` |
| **copyright** | `3` | Source material / franchise | `fate/stay_night`, `touhou`, `genshin_impact` |
| **character** | `4` | Specific character names | `hatsune_miku`, `hakurei_reimu` |
| **meta** | `5` | Image quality / format tags | `highres`, `absurdres`, `commentary` |

**Tips:**
- For generating images, focus on **general** tags (colours, poses, clothing, expressions)
- Add **character** and **copyright** tags when depicting a specific character
- **meta** tags like `highres` and `best_quality` can improve output quality
- Avoid **artist** tags unless intentionally mimicking a specific art style

---

## Best Practices

### ✅ Always validate before generating

```python
# Always run this before finalising
result = validate_tags(your_proposed_tags)
# Fix everything in result["invalid"] before sending to ComfyUI
```

### ✅ Use suggest_tags for discoverability

Even for tags you think you know, run `suggest_tags` to find the canonical form:
- `standing` vs `standing_on_one_leg` vs `standing_split`
- `smile` vs `small_smile` vs `evil_smile`

The tag with the highest `post_count` is almost always the right one for your intent.

### ✅ Prefer high-post-count tags

Higher post count = more training data = more consistent model response.

```python
# Get the top 5 most established hair colour tags
suggest_tags("hair_color", limit=5, category="general")
```

### ✅ Layer specificity

Good prompts move from general to specific:
```
# General → Specific
1girl,                        # subject count
solo,                         # composition
long_hair, blue_hair,         # hair
white_dress, off_shoulder,    # clothing  
smile, looking_at_viewer,     # expression/pose
outdoors, garden, daytime,    # setting
masterpiece, best_quality     # quality
```

### ❌ Avoid deprecated tags

If `validate_tags` reports a tag as `deprecated`, use `suggest_tags` to find the current replacement:

```python
# If "nude" is deprecated, find the current tag:
suggest_tags("nude", category="general")
```

### ❌ Don't invent tags

The model doesn't understand arbitrary natural language in prompts — only tags it was trained on. `beautiful_landscape` is not a Danbooru tag; `scenery` and `landscape` are.

---

## Common Scenarios

### Scenario: Character in a specific pose

```
# 1. Search for pose tags
search_tags("sitting", category="general", limit=10)
→ sitting, sitting_on_ground, kneeling, seiza, wariza, …

# 2. Validate the full tag set
validate_tags(["1girl", "hatsune_miku", "sitting", "looking_at_viewer", "smile"])
```

### Scenario: Specific art style

```
# Find copyright tags for a franchise
search_tags("genshin", category="copyright", limit=5)
→ genshin_impact, …

# Find character from that franchise  
search_tags("hu_tao", category="character", limit=3)
→ hu_tao_(genshin_impact), …
```

### Scenario: Quality boosting tags

```
# Find commonly used meta/quality tags
search_tags("quality", category="meta", limit=5)
→ best_quality, high_quality, …

search_tags("res", category="meta", limit=5)  
→ highres, absurdres, ultra-high_res, …
```

### Scenario: Unknown misspelling

```
# You typed "haor" instead of "hair"
suggest_tags("haor", limit=5)
→ [] (no prefix match)

# Try a broader search
search_tags("long hair")
→ long_hair, long_hair_between_eyes, wavy_hair, …
```

---

## Troubleshooting

### "invalid" tags that should be valid

The database contains only tags with **≥10 posts**. Tags with fewer posts are intentionally excluded as they are likely misspellings, very niche, or one-off annotations.

If a tag you expect to be valid shows as invalid:
1. Try `suggest_tags` to find a close variant
2. Use `search_tags` to explore the tag space
3. The tag may genuinely have <10 posts — use a broader synonym instead

### Server not responding

Check the MCP server is running and the `db/tags.db` file exists:

```bash
# Local
python src/server.py

# Docker
docker run --rm -i danbooru-mcp:latest
```

Environment variable override:
```bash
DANBOORU_TAGS_DB=/custom/path/tags.db python src/server.py
```

### Database needs rebuilding / updating

Re-run the scraper (it's resumable):

```bash
# Refresh all tags
python scripts/scrape_tags.py --no-resume

# Update changed tags only (re-scrapes from scratch, stops at ≥10 posts boundary)
python scripts/scrape_tags.py
```

Then rebuild the Docker image:
```bash
docker build -f Dockerfile.prebuilt -t danbooru-mcp:latest .
```
