SAkizuki commited on
Commit
54316b6
·
verified ·
1 Parent(s): 81d2413

Auto-sync from GitHub Actions

Browse files
Files changed (1) hide show
  1. mcp_server.py +41 -126
mcp_server.py CHANGED
@@ -50,147 +50,61 @@ mcp = FastMCP(
50
  @mcp.tool()
51
  async def search_tags(
52
  query: str,
53
- use_segmentation: bool = True,
54
- top_k: int = 5,
55
- limit: int = 80,
56
- popularity_weight: float = 0.15,
57
  show_nsfw: bool = True,
58
  include_wiki: bool = False,
59
- category: str = "all",
60
- group_mode: str = "off",
61
- max_per_group: int = 2,
62
  ) -> str:
63
  """
64
  Search Danbooru tags using natural language and return a ready-to-use prompt.
 
65
 
66
  ## Args
67
  - query: Natural language description (Chinese recommended).
68
- - use_segmentation: Split multi-concept input into segments for separate retrieval. True for scene descriptions, False for single-concept queries.
69
- - top_k: Candidates recalled per segment. Semantics change with use_segmentation — see guide below.
70
- - limit: Max tags returned.
71
- - popularity_weight: Influence of tag post count on ranking (0.0–1.0). Default 0.15.
 
 
 
 
 
 
72
  - show_nsfw: Include NSFW tags. Default True.
73
  - include_wiki: Append wiki description to each result. Default False.
74
- - category: Filter results to a specific tag category. Default "all".
75
- "all" — All categories (通用 + 版权 + 人物 )
76
- "general" — General: visual attributes, clothing, pose, background, etc.
77
- "copyright" — Copyright: specific anime/game/franchise titles
78
- "character" — Character: named characters from any series
79
- Use this when you know what kind of tag you need — e.g. looking for a
80
- character name vs. describing a scene visually.
81
- - group_mode: Tag group processing mode. Default "off".
82
- "off" — No group processing (backward compatible)
83
- "expand" — Boost same-group tags for concept exploration
84
- "diverse" — Limit tags per group for scene diversity
85
- - max_per_group: Max tags per group in diverse mode. Default 2.
86
 
87
  ## Query writing guide
88
 
89
- The `query` parameter supports explicit delimiter control for precise segmentation.
90
-
91
- ### Explicit delimiters
92
-
93
  Use **spaces, newlines, Chinese commas (,), or Chinese dunhao (、)** to manually separate concepts.
94
  Each delimiter-bounded segment ≤7 characters stays atomic — the engine respects your intent.
95
 
96
- | Query style | Example | When to use |
97
- |---|---|---|
98
- | Concept list (spaces) | `运动社团 校队 比赛 运动会` | You know the exact concepts to search |
99
- | Concept list (dun hao) | `反乌托邦、赛博朋克、蒸汽朋克` | Same, with Chinese list punctuation |
100
- | Natural sentence | `一个穿着白色水手服的少女在雨中奔跑` | Scene description, let the engine auto-split |
101
- | Mixed | `运动社团 一个穿水手服的少女` | Mix concepts with descriptive phrases |
102
-
103
- Segments >7 characters are still auto-split by jieba, but the raw segment is kept as an additional query
104
- to preserve clause-level semantics (multi-granularity retrieval).
105
-
106
- ### Recommendations
107
-
108
- 1. **Concept lists → use explicit delimiters:** Group independent concepts with spaces or dunhao.
109
- `运动社团 校队 比赛 体育祭 田径部` is better than `运动社团校队比赛体育祭田径部`.
110
-
111
- 2. **Scene descriptions → write naturally:** Natural Chinese with Chinese commas works well for full scenes.
112
- `一个穿着白色水手服,蓝色短裙的少女在雨中奔跑` — commas here are grammatical, not delimiters.
113
-
114
- 3. **Precise lookup → turn off segmentation:** For finding a specific character or copyright title,
115
- set `use_segmentation=False` and combine with `category` filter.
116
- e.g. `query="EVA中蓝发的零号机驾驶员"` with `category="character"` and `use_segmentation=False`.
117
-
118
- 4. **Category filtering:** Use `category` to narrow results. Looking for a character?
119
- `category="character"`. Building a scene prompt? `category="general"`.
120
-
121
- ## Parameter guide
122
-
123
- ### Step 1 — Decide use_segmentation + top_k together
124
-
125
- top_k means "candidates per segment"; its effect depends on whether segmentation is on.
126
-
127
- Multi-concept input (scene description) → use_segmentation=True
128
-
129
- | Sub-scenario | top_k | Reason |
130
- |-----------------------|-------|----------------------------------------------------------|
131
- | Full scene → prompt | 5 | Many segments; low top_k distributes result slots fairly |
132
- | Vague concept explore | 80 | Few segments; high top_k needed for broad recall |
133
-
134
- Single-concept input → use_segmentation=False
135
-
136
- top_k acts as total candidate pool size. Use 20 for all single-concept cases.
137
 
138
- | Sub-scenario | top_k |
139
- |-------------------------------|-------|
140
- | Describe subject / find tag | 20 |
141
- | Precise lookup / spell fix | 20 |
142
-
143
- ### Step 2 — Decide limit independently
144
-
145
- | Goal | limit |
146
- |------------------------------|-------|
147
- | Full prompt for image gen | 80 |
148
- | Concept exploration | 20–80 |
149
- | Precise lookup / role search | 10–20 |
150
-
151
- ### Auxiliary params
152
-
153
- popularity_weight (default 0.15, rarely needs changing):
154
- - Higher (0.3+): favor common, well-established tags
155
- - Lower (0.0): surface niche/rare tags
156
-
157
- include_wiki (default False):
158
- - True: The meaning of the tag is important — disambiguation, explaining tags to users, exploring unfamiliar domains, or when you are unsure of the tag's meaning
159
- - False: Prompt generation (Wiki is irrelevant to the downstream task), tags are known
160
-
161
- ### Quick reference
162
-
163
- | Scenario | use_segmentation | top_k | limit |
164
- |-------------------------------|------------------|-------|-------|
165
- | Full scene → prompt (default) | True | 5 | 80 |
166
- | Vague concept exploration | True | 80 | 80 |
167
- | Describe subject / find tag | False | 20 | 20 |
168
- | Precise lookup / spell fix | False | 20 | 10 |
169
-
170
- ### Workflow
171
-
172
- After search_tags, pass selected tags to get_related_tags to discover complementary tags via co-occurrence (accessories, character features, scene atmosphere).
173
- Supports chained exploration / iterative loops – take the interesting tags from the returned results as input to call get_related_tags again,
174
- and use the results from get_related to feed back into a new round of search, enabling multi-hop deep traversal along the co-occurrence graph.
175
-
176
- ## Examples
177
-
178
- Precise lookup / spell fix — e.g. "selafuku", "thighhigh", "twintail"
179
- → use_segmentation=False, top_k=20, limit=10
180
-
181
- Vague concept exploration — e.g. "兔耳朵", "赛博朋克服装", "假肢"
182
- → use_segmentation=True, top_k=80, limit=80
183
-
184
- Describe subject / find tag — e.g. "EVA中蓝发的零号机驾驶员", "命运石之门中的助手"
185
- → use_segmentation=False, top_k=20, limit=20
186
 
187
- Full scene prompt e.g. "一个穿着白色水手服,蓝色短裙的少女在雨中的城市里奔跑"
188
- use_segmentation=True, top_k=5, limit=80
189
 
190
  ## Returns
 
191
  JSON with: prompt (comma-separated tags), keywords, results.
192
  Each result: tag, cn_name, category, final_score, count[, wiki if include_wiki=True].
193
  """
 
 
 
 
 
 
 
 
194
  _CATEGORY_MAP: dict[str, list[str]] = {
195
  "all": ["General", "Character", "Copyright", "Artist", "Meta"],
196
  "general": ["General"],
@@ -199,20 +113,20 @@ Each result: tag, cn_name, category, final_score, count[, wiki if include_wiki=T
199
  }
200
  target_categories = _CATEGORY_MAP.get(
201
  category,
202
- _CATEGORY_MAP["all"], # unrecognized value → fall back to all
203
  )
204
 
205
  tagger = await DanbooruTagger.get_instance()
206
  request = SearchRequest(
207
  query=query,
208
- top_k=top_k,
209
- limit=limit,
210
- popularity_weight=popularity_weight,
211
  show_nsfw=show_nsfw,
212
- use_segmentation=use_segmentation,
213
  target_categories=target_categories,
214
- group_mode=group_mode,
215
- max_per_group=max_per_group,
216
  )
217
  response = await tagger.search_async(request)
218
  # 计数:每次 MCP 搜索调用均计入搜索、成功、复制;访问不变
@@ -259,6 +173,7 @@ async def get_related_tags(
259
  ) -> str:
260
  """
261
  Return co-occurrence-based tag recommendations for a given tag list (NPMI scoring).
 
262
 
263
  This tool surfaces tags that frequently appear alongside the seeds in
264
  Danbooru, mixing categories (General / Character / Copyright) by design.
@@ -370,7 +285,7 @@ JSON array sorted by aggregated NPMI score (descending). Each result:
370
  item["wiki"] = r.wiki
371
  output.append(item)
372
 
373
- payload = output
374
  if corrections:
375
  correction_notes = [
376
  f"{bad} → {good}" for bad, good in corrections.items()
 
50
  @mcp.tool()
51
  async def search_tags(
52
  query: str,
53
+ search_mode: str = "full_scene",
54
+ category: str = "all",
 
 
55
  show_nsfw: bool = True,
56
  include_wiki: bool = False,
 
 
 
57
  ) -> str:
58
  """
59
  Search Danbooru tags using natural language and return a ready-to-use prompt.
60
+ Only supported for general, copyright, and character tag searches; **artists and meta tags are not supported.**
61
 
62
  ## Args
63
  - query: Natural language description (Chinese recommended).
64
+ - search_mode: Preset strategy. Pick the one that matches your intent.
65
+ "full_scene" — Full scene prompt (e.g. "一个穿着白色水手服的少女在雨中奔跑")
66
+ "concept_explore" — Vague concept exploration, broad recall (e.g. "赛博朋克服装", "兔耳朵", "中国风汉服")
67
+ "subject_describe" Describe a subject to find matching tags (e.g. "EVA中蓝发的驾驶员", "两侧有开口,前方有拉绳的运动短裤")
68
+ "precise_lookup" — Precise lookup / spell fix (e.g. "selafuku", "thighhigh")
69
+ - category: Filter to a specific tag category. Default "all".
70
+ "all" — All (通用 + 版权 + 人物)
71
+ "general" — Visual attributes, clothing, pose, background, etc.
72
+ "character" — Named characters from any series
73
+ "copyright" — Specific anime/game/franchise titles
74
  - show_nsfw: Include NSFW tags. Default True.
75
  - include_wiki: Append wiki description to each result. Default False.
76
+ Set True when tags are unfamiliar and need disambiguation.
 
 
 
 
 
 
 
 
 
 
 
77
 
78
  ## Query writing guide
79
 
 
 
 
 
80
  Use **spaces, newlines, Chinese commas (,), or Chinese dunhao (、)** to manually separate concepts.
81
  Each delimiter-bounded segment ≤7 characters stays atomic — the engine respects your intent.
82
 
83
+ | Query style | Example |
84
+ |---|---|
85
+ | Concept list (spaces) | `运动社团 校队 比赛 运动会` |
86
+ | Concept list (dun hao) | `反乌托邦、赛博朋克、蒸汽朋克` |
87
+ | Natural sentence | `一个穿着白色水手服的少女在雨中奔跑` |
88
+ | Mixed | `运动社团 一个穿水手服的少女` |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
+ ## Workflow
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
+ After search_tags, pass selected tags to get_related_tags to discover complementary tags via co-occurrence.
93
+ Chain freely: search_tags get_related_tags get_related_tags → search_tags for multi-hop exploration.
94
 
95
  ## Returns
96
+
97
  JSON with: prompt (comma-separated tags), keywords, results.
98
  Each result: tag, cn_name, category, final_score, count[, wiki if include_wiki=True].
99
  """
100
+ _SEARCH_MODE_PRESETS: dict[str, dict] = {
101
+ "precise_lookup": {"top_k": 10, "limit": 10, "popularity_weight": 0.15, "use_segmentation": False, "group_mode": "off", "max_per_group": 2},
102
+ "concept_explore": {"top_k": 80, "limit": 80, "popularity_weight": 0.15, "use_segmentation": True, "group_mode": "expand", "max_per_group": 2},
103
+ "subject_describe": {"top_k": 20, "limit": 20, "popularity_weight": 0.15, "use_segmentation": False, "group_mode": "off", "max_per_group": 2},
104
+ "full_scene": {"top_k": 5, "limit": 80, "popularity_weight": 0.15, "use_segmentation": True, "group_mode": "diverse", "max_per_group": 2},
105
+ }
106
+ preset = _SEARCH_MODE_PRESETS.get(search_mode, _SEARCH_MODE_PRESETS["full_scene"])
107
+
108
  _CATEGORY_MAP: dict[str, list[str]] = {
109
  "all": ["General", "Character", "Copyright", "Artist", "Meta"],
110
  "general": ["General"],
 
113
  }
114
  target_categories = _CATEGORY_MAP.get(
115
  category,
116
+ _CATEGORY_MAP["all"],
117
  )
118
 
119
  tagger = await DanbooruTagger.get_instance()
120
  request = SearchRequest(
121
  query=query,
122
+ top_k=preset["top_k"],
123
+ limit=preset["limit"],
124
+ popularity_weight=preset["popularity_weight"],
125
  show_nsfw=show_nsfw,
126
+ use_segmentation=preset["use_segmentation"],
127
  target_categories=target_categories,
128
+ group_mode=preset["group_mode"],
129
+ max_per_group=preset["max_per_group"],
130
  )
131
  response = await tagger.search_async(request)
132
  # 计数:每次 MCP 搜索调用均计入搜索、成功、复制;访问不变
 
173
  ) -> str:
174
  """
175
  Return co-occurrence-based tag recommendations for a given tag list (NPMI scoring).
176
+ Only supported for general, copyright, and character tag searches; **artists and meta tags are not supported.**
177
 
178
  This tool surfaces tags that frequently appear alongside the seeds in
179
  Danbooru, mixing categories (General / Character / Copyright) by design.
 
285
  item["wiki"] = r.wiki
286
  output.append(item)
287
 
288
+ payload = {"results": output}
289
  if corrections:
290
  correction_notes = [
291
  f"{bad} → {good}" for bad, good in corrections.items()